{*******************************************************************************
* The contents of this file are used with permission, subject to the Mozilla   *
* Public License Version 1.1 (the "License"); you may not use this file except *
* in compliance with the License. You may obtain a copy of the License at      *
* http://www.mozilla.org/MPL/                                                  *
*                                                                              *
* Software distributed under the License is distributed on an "AS IS" basis,   *
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for *
* the specific language governing rights and limitations under the License.    *
*                                                                              *
* The Original Code is distributed as part of the "mediate" product and is     *
* Copyright (C) @home media limited. All Rights Reserved.                      *
*                                                                              *
* Email: support@athomemedia.co.uk                                             *
* Web:   http://www.athomemedia.co.uk                                          *
*******************************************************************************}

unit Designer;

interface

uses
  DesignerManager, UIObjects, AHMTypes, AHMConsts,
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, CustomizeDlg, ActnMan, ActnList, StdCtrls, ExtCtrls, ImgList,
  ComCtrls, JvComponentBase, AppEvnts, Menus, StdActns, StdStyleActnCtrls,
  ToolWin, VirtualTrees, JvInspector, PngImageList, JvEditorCommon, JvHLEditor;

const
  CAPTION_PREFIX = 'mediate designer - ';
  CAPTION_NO_THEME = 'Copyright @home media limited 2006-2009';
  HINT_DESC_BASE = 'Base descriptor contains common theme elements that may ' +
                   'be referenced in other descriptors and is always loaded.';
  HINT_DESC_LOAD = ' is currently loaded.';
  HINT_DESC_UNLOAD = ' is not loaded.';
  HINT_DESCRIPTORS = 'Available descriptors for theme';
  HINT_VISUALS = 'Visual elements for theme';
  HINT_INTERFACE = 'Interface elements for theme';
  HINT_LANGUAGES = 'Language elements for theme';
  HINT_AUDIO = 'Audio elements for theme';
  HINT_PLUGINS = 'Available mediate plugins for all themes';
  HINT_MOVE_TO_BASE = 'Move object to base descriptor';
  HINT_MOVE_TO_CUSTOM = 'Move object to current custom descriptor';
  HINT_SELECT_BASE = 'Select base descriptor';
  HINT_SELECT_CUSTOM = 'Select custom descriptor';
  HINT_MOUSE_COORDS = 'Current mouse position';
  HINT_TOOL_COORDS = 'Current tool status';
  HINT_TOOL_HELP = 'Help hints for current tool';
  STATUS_TOOL_MOUSE = 'Use mouse or keyboard to interract with mediate';
  STATUS_TOOL_CNTNR = 'Click to add a new container to current screen';
  STATUS_TOOL_CNTRL = 'Click to add a new control to current container';
  STATUS_TOOL_SCREEN = 'Click to add a new screen to current descriptor';
  STATUS_TOOL_PAN = 'Use mouse or keyboard to move mediate display';
  STATUS_TOOL_EDIT = 'Hold ALT while clicking to select behind. Use cursor keys to ' +
                     'move, hold CTRL to adjust size, hold SHIFT to move quickly';
  STATUS_TOOL_ROTATE = 'Hold ALT while clicking to select behind. Use cursor keys ' +
                       'to rotate, hold CTRL for Z rotation, hold SHIFT to rotate quickly';
  STATE_IMG_BASE = 0; // Base descriptor state image index
  STATE_IMG_CUSTOM = 1; // Custom descriptor state image index
  STATE_IMG_LIB = 2; // Library descriptor state image index
  STATE_IMG_WARN = 3; // Warning state image index
  STATE_IMG_ERROR = 4; // Error state image index
  STATE_IMG_OBJECTS = 5; // Number of state images for object nodes
  STATE_IMG_CTRLS = 15; // Number of state images for controls
  STATE_IMG_ACTIONS = STATE_IMG_OBJECTS + STATE_IMG_CTRLS - 1; // Offset for actions
  ROOT_NODE_COUNT = 6; // Number of root nodes in tree

type
  TElementType = (etDescriptors, etVisual, etInterface, etAudio, etText, etPlugin, etNone);
  TElementTotals = array[TElementType] of Integer;

  PElementData = ^RElementData;
  RElementData = Record
    Category: TElementType;
    Action: TAction;
    ActionList: TActionList;
    Modified: Boolean;
  end;

  TAHMDesignerForm = class(TForm)
    ActionManager: TActionManager;
    sbBottomStatus: TStatusBar;
    acCut: TEditCut;
    acOpen: TAction;
    acAbout: TAction;
    acHyperlink: TAction;
    acNew: TAction;
    acDelete: TAction;
    acCopy: TAction;
    acPaste: TAction;
    odDescriptor: TOpenDialog;
    pnTopClientArea: TPanel;
    pnLeftPanel: TPanel;
    mmMainMenu: TMainMenu;
    miFile: TMenuItem;
    miExit: TMenuItem;
    ApplicationEvents1: TApplicationEvents;
    ActionListAudio: TActionList;
    acSounds: TAction;
    ActionListLanguages: TActionList;
    acLanguages: TAction;
    ActionListVisuals: TActionList;
    acColours: TAction;
    acFills: TAction;
    acPlasma: TAction;
    acFonts: TAction;
    acBitmaps: TAction;
    acBackgrounds: TAction;
    acForegrounds: TAction;
    acShapes: TAction;
    acCaptions: TAction;
    acMediaWindows: TAction;
    acBars: TAction;
    acButtons: TAction;
    acMediaGrids: TAction;
    ActionListInterfaces: TActionList;
    acFunctions: TAction;
    acMacros: TAction;
    acActions: TAction;
    acMetaInfo: TAction;
    acPlaces: TAction;
    acAnimators: TAction;
    acLayouts: TAction;
    acControls: TAction;
    acContainers: TAction;
    acScreens: TAction;
    acExit: TAction;
    SplitterDescriptors: TSplitter;
    popTreeObjects: TPopupMenu;
    miPreview: TMenuItem;
    miRun: TMenuItem;
    acLaunch: TAction;
    acShutdown: TAction;
    miLaunch: TMenuItem;
    miShutdown: TMenuItem;
    miHelp: TMenuItem;
    miAbout: TMenuItem;
    miVisit: TMenuItem;
    acConfigure: TAction;
    miConfigure: TMenuItem;
    acExpressions: TAction;
    pgcPreviewType: TPageControl;
    tsMediatePreview: TTabSheet;
    tsXML: TTabSheet;
    tsPreview: TTabSheet;
    pbPreview: TPaintBox;
    acGrids: TAction;
    acTextEdits: TAction;
    acClose: TAction;
    acSave: TAction;
    acSaveAs: TAction;
    miOpen: TMenuItem;
    miSave: TMenuItem;
    N1: TMenuItem;
    tsScratch: TTabSheet;
    mmScratch: TMemo;
    pnXMLFooter: TPanel;
    btApply: TButton;
    btRevert: TButton;
    acApply: TAction;
    acRevert: TAction;
    acTimers: TAction;
    pnDescriptorList: TPanel;
    SplitterProperties: TSplitter;
    pnProperties: TPanel;
    tsConsole: TTabSheet;
    mmConsoleLog: TMemo;
    ecConsoleCmd: TComboBox;
    mmConsoleWatches: TMemo;
    SplitterConsole: TSplitter;
    acEdit: TAction;
    acRestart: TAction;
    miRestart: TMenuItem;
    tbTopToolbar: TToolBar;
    acMouseTool: TAction;
    acNewContainer: TAction;
    acNewControl: TAction;
    acNewScreen: TAction;
    acEditTool: TAction;
    acRotateTool: TAction;
    acNewObject: TAction;
    acBaseDescriptor: TAction;
    miEdit: TMenuItem;
    miNewObject: TMenuItem;
    miDeleteObject: TMenuItem;
    acUndo: TAction;
    miUndo: TMenuItem;
    acRedo: TAction;
    miRedo: TMenuItem;
    miNewTheme: TMenuItem;
    miSaveThemeAs: TMenuItem;
    acBorders: TAction;
    acEffects: TAction;
    Copy1: TMenuItem;
    Paste1: TMenuItem;
    N2: TMenuItem;
    N3: TMenuItem;
    N4: TMenuItem;
    N5: TMenuItem;
    Cut1: TMenuItem;
    Cut2: TMenuItem;
    Copy2: TMenuItem;
    Paste2: TMenuItem;
    N6: TMenuItem;
    DeleteObject1: TMenuItem;
    NewObject1: TMenuItem;
    EditObject1: TMenuItem;
    BaseDescriptor1: TMenuItem;
    acBack: TAction;
    acForward: TAction;
    View1: TMenuItem;
    ools1: TMenuItem;
    Mouse1: TMenuItem;
    SelectContainer1: TMenuItem;
    SelectControl1: TMenuItem;
    SelectScreen1: TMenuItem;
    AdjustPosition1: TMenuItem;
    AdjustSize1: TMenuItem;
    DisplayBorders1: TMenuItem;
    N8: TMenuItem;
    N9: TMenuItem;
    N10: TMenuItem;
    pnInspectorHeader: TPanel;
    pbHeader: TPaintBox;
    acPluginWindows: TAction;
    tcRenderer: TTabControl;
    tbMediateToolbar: TToolBar;
    pnMediateFrame: TPanel;
    pnMediatePreview: TPanel;
    popPreview: TPopupMenu;
    CloseTab1: TMenuItem;
    acGauges: TAction;
    acDials: TAction;
    acCloseTab: TAction;
    acMoveObject: TAction;
    miMoveObject: TMenuItem;
    popPlugins: TPopupMenu;
    acLoadPlugin: TAction;
    acUnloadPlugin: TAction;
    acUpdatePlugin: TAction;
    acStartPlugin: TAction;
    acStopPlugin: TAction;
    miLoadPlugin: TMenuItem;
    miUnloadPlugin: TMenuItem;
    N11: TMenuItem;
    miStartPlugin: TMenuItem;
    miStopPlugin: TMenuItem;
    miUpdatePlugin: TMenuItem;
    acConfigurePlugin: TAction;
    N12: TMenuItem;
    miConfigurePlugin: TMenuItem;
    Plugins1: TMenuItem;
    LoadPlugin1: TMenuItem;
    UnloadPlugin1: TMenuItem;
    N13: TMenuItem;
    StartPlugin1: TMenuItem;
    StopPlugin1: TMenuItem;
    UpdatePlugin1: TMenuItem;
    N14: TMenuItem;
    ConfigurePlugin1: TMenuItem;
    MoveObject1: TMenuItem;
    acGridItems: TAction;
    miHistory: TMenuItem;
    Back2: TMenuItem;
    Forward2: TMenuItem;
    miHistoryRoot: TMenuItem;
    acErrors: TAction;
    ErrorLog1: TMenuItem;
    acOptions: TAction;
    N7: TMenuItem;
    Options1: TMenuItem;
    acThemeProperties: TAction;
    N15: TMenuItem;
    Properties1: TMenuItem;
    Close1: TMenuItem;
    acRenameObject: TAction;
    RenameObject1: TMenuItem;
    RenameObject2: TMenuItem;
    popDescriptors: TPopupMenu;
    MenuItem2: TMenuItem;
    MenuItem3: TMenuItem;
    MenuItem4: TMenuItem;
    MenuItem5: TMenuItem;
    MenuItem7: TMenuItem;
    MenuItem8: TMenuItem;
    acFindObject: TAction;
    acFindAgain: TAction;
    N16: TMenuItem;
    FindObject1: TMenuItem;
    FindAgain1: TMenuItem;
    acRemoveSelection: TAction;
    acPanTool: TAction;
    acZoomIn: TAction;
    acZoomOut: TAction;
    acZoomFull: TAction;
    N17: TMenuItem;
    RemoveSelection1: TMenuItem;
    PanDisplay1: TMenuItem;
    ZoomFull1: TMenuItem;
    ZoomIn1: TMenuItem;
    ZoomOut1: TMenuItem;
    acExportTheme: TAction;
    N18: TMenuItem;
    ExportTheme1: TMenuItem;
    acImportTheme: TAction;
    ImportTheme1: TMenuItem;
    acRevertObject: TAction;
    RevertObject1: TMenuItem;
    RevertObject2: TMenuItem;
    EditObject2: TMenuItem;
    acPreviewObject: TAction;
    PreviewObject1: TMenuItem;
    PreviewObject2: TMenuItem;
    procedure pnMediatePreviewDragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure pnMediatePreviewDragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
    procedure acRevertObjectUpdate(Sender: TObject);
    procedure acRevertObjectExecute(Sender: TObject);
    procedure acImportThemeExecute(Sender: TObject);
    procedure acExportThemeUpdate(Sender: TObject);
    procedure acExportThemeExecute(Sender: TObject);
    procedure acNewScreenExecute(Sender: TObject);
    procedure acZoomFullExecute(Sender: TObject);
    procedure acZoomOutExecute(Sender: TObject);
    procedure acZoomInExecute(Sender: TObject);
    procedure acRemoveSelectionExecute(Sender: TObject);
    procedure acRenameObjectUpdate(Sender: TObject);
    procedure acRenameObjectExecute(Sender: TObject);
    procedure acNewExecute(Sender: TObject);
    procedure acExitExecute(Sender: TObject);
    procedure acOpenExecute(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);
    procedure vstThemeGetNodeDataSize(Sender: TBaseVirtualTree;
      var NodeDataSize: Integer);
    procedure vstThemeInitNode(Sender: TBaseVirtualTree;
      ParentNode, Node: PVirtualNode;
      var InitialStates: TVirtualNodeInitStates);
    procedure vstThemeInitChildren(Sender: TBaseVirtualTree;
      Node: PVirtualNode; var ChildCount: Cardinal);
    procedure vstThemeGetText(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
      var CellText: WideString);
    procedure vstThemeMeasureItem(Sender: TBaseVirtualTree;
      TargetCanvas: TCanvas; Node: PVirtualNode; var NodeHeight: Integer);
    procedure vstThemeGetImageIndex(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
      var Ghosted: Boolean; var ImageIndex: Integer);
    procedure vstThemeChange(Sender: TBaseVirtualTree;
      Node: PVirtualNode);
    procedure vstThemeStartDrag(Sender: TObject; var DragObject: TDragObject);
    procedure vstThemeDragAllowed(Sender: TBaseVirtualTree; Node: PVirtualNode;
      Column: TColumnIndex; var Allowed: Boolean);
    procedure vstThemeEndDrag(Sender, Target: TObject; X, Y: Integer);
    procedure vstThemeDblClick(Sender: TObject);
    procedure acLaunchExecute(Sender: TObject);
    procedure acShutdownExecute(Sender: TObject);
    procedure acConfigureExecute(Sender: TObject);
    procedure acAboutExecute(Sender: TObject);
    procedure acHyperlinkExecute(Sender: TObject);
    procedure acSaveExecute(Sender: TObject);
    procedure acApplyExecute(Sender: TObject);
    procedure acRevertExecute(Sender: TObject);
    procedure pnMediateFrameResize(Sender: TObject);
    procedure pnMediatePreviewMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure pnMediatePreviewMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure pnMediatePreviewMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure mmConsoleLogChange(Sender: TObject);
    procedure pgcPreviewTypeChange(Sender: TObject);
    procedure ecConsoleCmdKeyPress(Sender: TObject; var Key: Char);
    procedure acEditExecute(Sender: TObject);
    procedure acRestartExecute(Sender: TObject);
    procedure acToolSelection(Sender: TObject);
    procedure InspectorItemValueChanged(Sender: TObject;
      Item: TJvCustomInspectorItem);
    procedure acNewObjectExecute(Sender: TObject);
    procedure acNewObjectUpdate(Sender: TObject);
    procedure vstThemeChecked(Sender: TBaseVirtualTree; Node: PVirtualNode);
    procedure vstThemePaintText(Sender: TBaseVirtualTree;
      const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
      TextType: TVSTTextType);
    procedure acSaveUpdate(Sender: TObject);
    procedure acLaunchUpdate(Sender: TObject);
    procedure acShutdownUpdate(Sender: TObject);
    procedure acRestartUpdate(Sender: TObject);
    procedure acBaseDescriptorExecute(Sender: TObject);
    procedure acBaseDescriptorUpdate(Sender: TObject);
    procedure acDeleteExecute(Sender: TObject);
    procedure acDeleteUpdate(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure pnMediatePreviewMouseEnter(Sender: TObject);
    procedure pnMediatePreviewMouseLeave(Sender: TObject);
    procedure FormKeyPress(Sender: TObject; var Key: Char);
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure InspectorItemValueChanging(Sender: TObject;
      Item: TJvCustomInspectorItem; var NewValue: string;
      var AllowChange: Boolean);
    procedure acUndoUpdate(Sender: TObject);
    procedure acUndoExecute(Sender: TObject);
    procedure acRedoExecute(Sender: TObject);
    procedure acRedoUpdate(Sender: TObject);
    procedure mmXMLChange(Sender: TObject);
    procedure mmXMLPaintGutter(Sender: TObject; Canvas: TCanvas);
    procedure acBordersExecute(Sender: TObject);
    procedure ApplicationEvents1ShowHint(var HintStr: string;
      var CanShow: Boolean; var HintInfo: THintInfo);
    procedure acCopyExecute(Sender: TObject);
    procedure acCopyUpdate(Sender: TObject);
    procedure acPasteExecute(Sender: TObject);
    procedure acPasteUpdate(Sender: TObject);
    procedure acCutExecute(Sender: TObject);
    procedure acCutUpdate(Sender: TObject);
    procedure vstThemeContextPopup(Sender: TObject; MousePos: TPoint;
      var Handled: Boolean);
    procedure acEditUpdate(Sender: TObject);
    procedure InspectorItemSelected(Sender: TObject);
    procedure InspectorItemDoubleClicked(Sender: TObject;
      Item: TJvCustomInspectorItem);
    procedure acBackExecute(Sender: TObject);
    procedure acBackUpdate(Sender: TObject);
    procedure acForwardExecute(Sender: TObject);
    procedure acForwardUpdate(Sender: TObject);
    procedure pbHeaderPaint(Sender: TObject);
    procedure miCustomActionClick(Sender: TObject);
    procedure miHistoryItemClick(Sender: TObject);
    procedure tcRendererChange(Sender: TObject);
    procedure acCloseTabExecute(Sender: TObject);
    procedure acCloseTabUpdate(Sender: TObject);
    procedure acMoveObjectExecute(Sender: TObject);
    procedure acMoveObjectUpdate(Sender: TObject);
    procedure acLoadPluginExecute(Sender: TObject);
    procedure acLoadPluginUpdate(Sender: TObject);
    procedure acUnloadPluginExecute(Sender: TObject);
    procedure acUnloadPluginUpdate(Sender: TObject);
    procedure acUpdatePluginExecute(Sender: TObject);
    procedure acUpdatePluginUpdate(Sender: TObject);
    procedure acStartPluginExecute(Sender: TObject);
    procedure acStartPluginUpdate(Sender: TObject);
    procedure acStopPluginExecute(Sender: TObject);
    procedure acStopPluginUpdate(Sender: TObject);
    procedure acConfigurePluginExecute(Sender: TObject);
    procedure acConfigurePluginUpdate(Sender: TObject);
    procedure acToolUpdate(Sender: TObject);
    procedure acErrorsExecute(Sender: TObject);
    procedure acErrorsUpdate(Sender: TObject);
    procedure acOptionsExecute(Sender: TObject);
    procedure FormMouseWheel(Sender: TObject; Shift: TShiftState;
      WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
    procedure acSaveAsExecute(Sender: TObject);
    procedure acSaveAsUpdate(Sender: TObject);
    procedure acThemePropertiesUpdate(Sender: TObject);
    procedure acThemePropertiesExecute(Sender: TObject);
    procedure acCloseExecute(Sender: TObject);
    procedure acCloseUpdate(Sender: TObject);
    procedure acFindObjectUpdate(Sender: TObject);
    procedure acFindObjectExecute(Sender: TObject);
    procedure acFindAgainUpdate(Sender: TObject);
    procedure acFindAgainExecute(Sender: TObject);
    procedure pnMediatePreviewDblClick(Sender: TObject);
    procedure acPreviewObjectUpdate(Sender: TObject);
    procedure acPreviewObjectExecute(Sender: TObject);
  private
    { Private declarations }
    FCaptionBase: String;
    FDragObject: TAHMUIObject;
    FDragRenderer: TAHMDragRenderer;
    FManager: TAHMDesignerManager;
    FLargeToolbarImages: TPngImageList;
    FSmallToolbarImages: TPngImageList;
    FLargeTreeImages: TPngImageList;
    FSmallTreeImages: TPngImageList;
    FThemeTree: TVirtualStringTree;
    FInspector: TJvInspector;
    FXMLEditor: TJvHLEditor;
    FDraggingTree: Boolean;
    FUpdatingTree: Boolean;
    FNavigatingTree: Boolean;
    FModifyingXML: Boolean;
    FPreviewSelected: Boolean;
    FInitialisedLayout: Boolean;
    function GetSelectedDescriptor: String;
    function GetSelectedObject: TAHMUIObject; overload;
    function GetSelectedObject(Node: PVirtualNode): TAHMUIObject; overload;
    function GetSelectedType: TAHMUIObjectType;
    function GetSelectedCategory: TElementType;
    function GetSelectedPreview: TAHMUIObject;
    function GetSelectedHint(Node: PVirtualNode; var Item: TAHMUIObject): String;
    function IsPluginSelected: Boolean;
    procedure ObjectAdded(Added: TAHMUIObject);
    procedure ObjectChanged(Changed: TAHMUIObject);
    procedure SelectionChanged(Selected: TAHMUIObject);
    procedure PreviewChanged(Sender: TObject);
    procedure NavigationChanged(Sender: TObject);
    procedure DescriptorLoaded(Descriptor: String);
    procedure DesignerToolSelected(Selection: TAHMToolType);
    procedure DesignerLaunched(Sender: TObject);
    procedure DesignerRestarted(Sender: TObject);
    procedure DesignerShutdown(Sender: TObject);
  protected
    procedure CMDialogKey(var Msg: TWMKey); message CM_DIALOGKEY;
    procedure WMSysCommand(var Msg: TWmSysCommand); message WM_SYSCOMMAND;
    procedure CreateParams(var Params: TCreateParams); override;
    procedure UpdateToolStatus;
    procedure UpdateMouseStatus;
    procedure UpdateStatus(const Status: String = '');
    procedure UpdateRenderer(Selection: TAHMUIObject);
    procedure UpdatePreviewControls;
    procedure InitialiseTheme;
    procedure CloseTheme;
    procedure UpdateCaption;
    procedure UpdateCursor;
    procedure UpdateFormCaption(Item: TAHMUIObject = nil);
    procedure DisplayNodeProperties(Item: TAHMUIObject);
    procedure UpdateXML(Item: TAHMUIObject);
    procedure UpdateItemMenus(Item: TAHMUIObject);
    procedure UpdateHistoryMenu;
    procedure RefreshDescriptor(ClearDisplay: Boolean = False);
    procedure FindSelection(SearchText: String);
    procedure UpdateSelection(Selection: TAHMUIObject; Rebuild: Boolean = False); overload;
    procedure UpdateSelection(Descriptor: String); overload;
    procedure UpdatePreviewTabs;
    procedure BuildCustomControls;
    procedure BuildToolbars;
    procedure FixControlStyles(Parent: TControl);
    procedure LoadImages;
    procedure ApplyPreferences(Loading: Boolean = False);
  public
    { Public declarations }
    property LargeTreeImages: TPngImageList read FLargeTreeImages;
    property SmallTreeImages: TPngImageList read FSmallTreeImages;
    function IsShortCut(var Message: TWMKey): Boolean; override;
  end;


var
  AHMDesignerForm: TAHMDesignerForm;

implementation

uses Math, DesignerPropsInspector, ApplicationManager, ThemeManager,
     BaseControls, Screen, DesignerHint, Utilities;

{$R *.dfm}

//-----------------------------------------------------------------------------
//--------------------------- Application Events ------------------------------
//-----------------------------------------------------------------------------

procedure TAHMDesignerForm.ApplicationEvents1Idle(Sender: TObject;
  var Done: Boolean);
begin
  if not FInitialisedLayout then
  begin
    // Initialise position of notebook
    pgcPreviewType.ActivePage := tsMediatePreview;

    // Force initial resize of mediate render panel
    pnMediateFrameResize(nil);

    FInitialisedLayout := True;
  end;

  // Render mediate user interface if required
  if Assigned(ApplicationManagerInstance) then
    if FDraggingTree or ApplicationManagerInstance.RenderFrame then
    begin
      // Update console log & command history
      if ApplicationManagerInstance.State = asRunning then
      begin
        mmConsoleLog.Text := ApplicationManagerInstance.UIManager.DebugConsole.UnwrappedOutput;
        mmConsoleWatches.Text := ApplicationManagerInstance.UIManager.DebugConsole.WatchesOutput;
      end
      else if ApplicationManagerInstance.State <> asPaused then
      begin
        mmConsoleLog.Lines.Clear;
        mmConsoleWatches.Lines.Clear;
      end;

      // Update mouse coordinates display
      UpdateMouseStatus;

      Done := False; // Keep looping

      // Since we are preventing idle loop exiting, we need to update actions
      Self.UpdateActions;
      Self.UpdateCaption;
      Self.UpdateCursor;
    end;
end;

procedure TAHMDesignerForm.ApplicationEvents1ShowHint(var HintStr: string;
  var CanShow: Boolean; var HintInfo: THintInfo);
var
  StatusRect: TRect;
  i: Integer;
  Node: PVirtualNode;
  List: TListItem;
  Combo: TComboBox;
  HintItem: TAHMUIObject;
begin
  // Display custom hints for each status panel
  if HintInfo.HintControl = sbBottomStatus then
  begin
    StatusRect := sbBottomStatus.ClientRect;
    StatusRect.Right := sbBottomStatus.Panels[0].Width;
    for i := 0 to Pred(sbBottomStatus.Panels.Count) do
    begin
      if StatusRect.Right > HintInfo.CursorPos.X then
      begin
        HintInfo.CursorRect := StatusRect;
        case i of
          0: HintStr := HINT_MOUSE_COORDS;
          1: HintStr := HINT_TOOL_COORDS;
          2: HintStr := HINT_TOOL_HELP;
        end;
        Exit;
      end;
      OffsetRect(StatusRect, sbBottomStatus.Panels[i].Width, 0);
    end;
  end;

  // Set default hint UIObject - none
  HintItem := nil;

  with HintInfo do
  begin
    // Lookup custom hint object for theme treeview
    if HintControl = FThemeTree then
    begin
      Node := FThemeTree.GetNodeAt(CursorPos.X, CursorPos.Y);
      if Node <> nil then
        HintStr := GetSelectedHint(Node, HintItem);
    end;

    // Lookup custom hint object for listviews
    if (HintControl is TListView) and (HintControl.Tag > 0) then
    begin
      List := TListView(HintControl).GetItemAt(CursorPos.X, CursorPos.Y);
      if (List <> nil) and (List.Data <> nil) then
      begin
        HintItem := TAHMUIObject(List.Data);
        HintStr := FManager.ItemHint(HintItem);
      end;
    end;

    // Lookup custom hint object for combos
    if HintControl is TComboBox then
    begin
      Combo := TComboBox(HintControl);
      if Combo.ItemIndex >= 0 then
        HintItem := TAHMUIObject(Combo.Items.Objects[Combo.ItemIndex]);
      if Assigned(HintItem) then
        HintStr := FManager.ItemHint(HintItem);
    end;

    // Store hint item in hint info
    HintData := HintItem;
  end;

  // Allow custom hint form to show hint first
  CanShow := not TfrmDesignerHint.ShowHint(HintInfo);
end;

//-----------------------------------------------------------------------------
//----------------------------- Form Events -----------------------------------
//-----------------------------------------------------------------------------

procedure TAHMDesignerForm.CreateParams(var Params: TCreateParams);
begin
  // Tweak window flags to make this appear as an app window on taskbar
  inherited CreateParams(Params);

  Params.ExStyle := Params.ExStyle and not WS_EX_TOOLWINDOW or WS_EX_APPWINDOW;
end;

procedure TAHMDesignerForm.FormCloseQuery(Sender: TObject;
  var CanClose: Boolean);
begin
  CanClose := FManager.ExitDesigner;
end;

procedure TAHMDesignerForm.FormCreate(Sender: TObject);
begin
  // Create global application manager instance
  TAHMApplicationManager.CreateInstance;
  ApplicationManagerInstance.DesignerMode := True;

  // Fix extra taskbar window under Vista
  RemoveApplicationWindowFromTaskBar;

  // Create long term designer manager instance
  FManager := TAHMDesignerManager.Create;
  FManager.Host := pnMediatePreview;
  FManager.OnLoadDescriptor := DescriptorLoaded;
  FManager.OnObjectAdded := ObjectAdded;
  FManager.OnObjectChanged := ObjectChanged;
  FManager.OnSelectionChanged := SelectionChanged;
  FManager.OnToolSelect := DesignerToolSelected;
  FManager.OnLaunch := DesignerLaunched;
  FManager.OnRestart := DesignerRestarted;
  FManager.OnShutdown := DesignerShutdown;
  FManager.Previews.OnRefresh := PreviewChanged;
  FManager.Navigation.OnRefresh := NavigationChanged;

  // Create and populate custom visual controls
  BuildCustomControls;
  BuildToolbars;
  LoadImages;

  // Fix control styles to display drag images
  FixControlStyles(Self);

  // Load designer preference settings
  FManager.LoadPreferences;
  ApplyPreferences(True);
end;

procedure TAHMDesignerForm.FormDestroy(Sender: TObject);
begin
  // Save configuration settings before we go
  FManager.Preferences.MainFormState.SaveFormState(Self);
  FManager.SavePreferences;

  // Free our application and designer manager instances
  TAHMApplicationManager.DestroyInstance;
  FManager.Free;
end;

procedure TAHMDesignerForm.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  // Pass keypress to designer if mouse is over preview area
  if FPreviewSelected and FManager.ProcessKeyPress(Key, Shift) then Key := 0;
end;

procedure TAHMDesignerForm.FormKeyPress(Sender: TObject; var Key: Char);
begin
  // Ignore all keypresses whilst mediate has focus
  if FPreviewSelected then Key := Chr(0);
end;

procedure TAHMDesignerForm.FormMouseWheel(Sender: TObject; Shift: TShiftState;
  WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
begin
  // Pass mouse wheel events to mediate whilst it has focus
  if FPreviewSelected and ApplicationManagerInstance.ProcessMouseWheel(WheelDelta) then
    Handled := True;
end;

function TAHMDesignerForm.IsShortCut(var Message: TWMKey): Boolean;
begin
  // Suppress Ctrl+X, Ctrl+C, Ctrl+V shortcuts when tree is not focused
  if (not FThemeTree.Focused) and (GetKeyState(VK_CONTROL) < 0) and
     ((Message.CharCode = Ord('C')) or (Message.CharCode = Ord('V')) or
     (Message.CharCode = Ord('X'))) then
    Result := False
  // Suppress Del shortcut when preview is not focused
  else if (not FPreviewSelected) and (Message.CharCode = VK_DELETE) then
    Result := False
  else
    Result := inherited IsShortCut(Message);
end;

//-----------------------------------------------------------------------------
//----------------------- VirtualTree Control Events --------------------------
//-----------------------------------------------------------------------------

procedure TAHMDesignerForm.vstThemeGetNodeDataSize(
  Sender: TBaseVirtualTree; var NodeDataSize: Integer);
begin
  NodeDataSize := SizeOf(RElementData);
end;

procedure TAHMDesignerForm.vstThemeInitNode(
  Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode;
  var InitialStates: TVirtualNodeInitStates);
var
  Data, ParentData: PElementData;
  Descriptor: String;
begin
  Data := Sender.GetNodeData(Node);
  ParentData := Sender.GetNodeData(ParentNode);

  case Sender.GetNodeLevel(Node) of
    0: begin
         // Theme descriptor categories
         Data.Category := TElementType(Node.Index);
         case Data.Category of
           etVisual:      Data.ActionList := ActionListVisuals;
           etInterface:   Data.ActionList := ActionListInterfaces;
           etText:        Data.ActionList := ActionListLanguages;
           etAudio:       Data.ActionList := ActionListAudio;
         end;
         InitialStates := InitialStates + [ivsHasChildren];
       end;
    1: begin
         // Descriptor list and item types
         Data.Category := ParentData.Category;
         Data.ActionList := ParentData.ActionList;

         if Assigned(Data.ActionList) then
         begin
           // UIObject list types
           Data.Action := TAction(Data.ActionList.Actions[Node.Index]);
           InitialStates := InitialStates + [ivsHasChildren];
         end
         else if ParentData.Category = etDescriptors then
         begin
           // Descriptor list
           Node.CheckType := VirtualTrees.ctRadioButton;
           Descriptor := FManager.ThemeModules[Node.Index];

           // Check selected descriptor
           if Descriptor = FManager.SelectedDescriptor then
             Node.CheckState := csCheckedNormal
           else
             Node.CheckState := csUncheckedNormal;
         end;
       end;
  end;
end;

procedure TAHMDesignerForm.vstThemeInitChildren(
  Sender: TBaseVirtualTree; Node: PVirtualNode; var ChildCount: Cardinal);
var
  Data: PElementData;
  List: TAHMUIObjectList;
begin
  Data := Sender.GetNodeData(Node);

  case Sender.GetNodeLevel(Node) of
    0: // Theme descriptor categories / Theme modules
       if Assigned(Data.ActionList) then
         ChildCount := Data.ActionList.ActionCount
       else if Data.Category = etPlugin then
         ChildCount := FManager.PluginCount
       else
         ChildCount := FManager.ThemeModules.Count;
    1: if Assigned(Data.Action) then
       begin
         // Get tag from action to determine editor type
         List := FManager.ObjectList[TAHMUIObjectType(Data.Action.Tag)];
         if Assigned(List) then ChildCount := List.TotalCount;
       end;
  end
end;

procedure TAHMDesignerForm.vstThemeGetText(
  Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
  TextType: TVSTTextType; var CellText: WideString);
var
  Data, ParentData: PElementData;
  Item: TAHMUIObject;
  List: TAHMUIObjectList;
begin
  Data := Sender.GetNodeData(Node);
  ParentData := Sender.GetNodeData(Node.Parent);

  // Set default text - empty
  CellText := '';

  case Sender.GetNodeLevel(Node) of
    0: if Column = 0 then
       begin
         // Top level nodes - theme descriptor categories
         case Data.Category of
           etDescriptors: CellText := 'Descriptors';
           etVisual:      CellText := 'Visuals';
           etInterface:   CellText := 'Interface';
           etText:        CellText := 'Languages';
           etAudio:       CellText := 'Audio';
           etPlugin:      CellText := 'Plugins';
         end;
       end;
    1: if Assigned(Data.Action) then
       begin
         // Theme descriptor heading
         if Column = 0 then CellText := Data.Action.Caption;
       end
       else if Data.Category = etPlugin then
       begin
         // Plugins
         if Column = 0 then CellText := FManager.Plugins[Node.Index].Name
                       else CellText := FManager.Plugins[Node.Index].Description;
         Data.Modified := FManager.Plugins[Node.Index].Modified;
       end
       else
       begin
         // Descriptor modules
         if Column = 0 then CellText := FManager.ThemeModules[Node.Index]
                       else CellText := FManager.ModuleDescriptions[Node.Index];
       end;
    2: if Assigned(ParentData.Action) then
       begin
         // Descriptor items
         List := FManager.ObjectList[TAHMUIObjectType(ParentData.Action.Tag)];
         if Assigned(List) then
         begin
           Item := TAHMUIObject(List.Objects[Node.Index]);
           if Assigned(Item) then
           begin
             case Column of
               0: CellText := Item.Name;
               1: CellText := Item.Description;
             end;
             Data.Modified := Item.Modified;
           end;
         end;
       end;
  end;
end;

procedure TAHMDesignerForm.vstThemeMeasureItem(
  Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode;
  var NodeHeight: Integer);
var
  ParentData: PElementData;
begin
  ParentData := Sender.GetNodeData(Node.Parent);

  // Increase node height for our theme descriptor headings
  if (Sender.GetNodeLevel(Node) = 1) and (ParentData.Category <> etDescriptors)
     and (ParentData.Category <> etPlugin) then
    NodeHeight := FLargeTreeImages.Height;
end;

procedure TAHMDesignerForm.vstThemePaintText(Sender: TBaseVirtualTree;
  const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
  TextType: TVSTTextType);
var
  Data, ParentData: PElementData;
  Item: TAHMUIObject;
  List: TAHMUIObjectList;
begin
  Data := Sender.GetNodeData(Node);
  ParentData := Sender.GetNodeData(Node.Parent);

  // Use coloured font for base descriptor items (unless selected)
  if FThemeTree.FocusedNode <> Node then
    case Sender.GetNodeLevel(Node) of
      1: if (ParentData.Category = etDescriptors) and (Node.Index = 0) then
           TargetCanvas.Font.Color := clActiveCaption;
      2: if Assigned(ParentData.Action) then
         begin
           // Descriptor items
           List := FManager.ObjectList[TAHMUIObjectType(ParentData.Action.Tag)];
           if Assigned(List) then
           begin
             Item := TAHMUIObject(List.Objects[Node.Index]);
             if Item.IsBaseObject then
             TargetCanvas.Font.Color := clActiveCaption;
           end;
         end;
    end;

  // Use italic font for modified items
  if Data.Modified then
    TargetCanvas.Font.Style := TargetCanvas.Font.Style + [fsItalic];
end;

procedure TAHMDesignerForm.vstThemeGetImageIndex(
  Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind;
  Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer);
var
  Data, ParentData: PElementData;
  Item: TAHMUIObject;
  List: TAHMUIObjectList;
begin
  Data := Sender.GetNodeData(Node);
  ParentData := Sender.GetNodeData(Node.Parent);

  if Column = 0 then
    case Kind of
      ikNormal, ikSelected:
        // Image for theme descriptor headings
        if Assigned(Data.Action) then ImageIndex := Data.Action.Tag + STATE_IMG_ACTIONS;
      ikState:
        if (ParentData <> nil) and Assigned(ParentData.Action) then
        begin
          // Image for descriptor item
          List := FManager.ObjectList[TAHMUIObjectType(ParentData.Action.Tag)];
          if Assigned(List) then
          begin
            Item := TAHMUIObject(List.Objects[Node.Index]);
            if Assigned(Item) and (Item.State = osValid) then
              if ParentData.Action = acControls then
                ImageIndex := Item.ImageIndex + STATE_IMG_OBJECTS
              else
                ImageIndex := ParentData.Action.Tag + STATE_IMG_ACTIONS
            else
              if (Item = nil) or (Item.State = osError) then ImageIndex := STATE_IMG_ERROR
              else ImageIndex := STATE_IMG_WARN; // Problem with object!
          end;
        end
        else if (Data.Category = etDescriptors) and (Sender.GetNodeLevel(Node) > 0) then
        begin
          // Theme module item - distinguish base/custom modules
          if Node.Index = 0 then ImageIndex := STATE_IMG_BASE
                            else ImageIndex := STATE_IMG_CUSTOM;
        end
        else if (Data.Category = etPlugin) and (Sender.GetNodeLevel(Node) > 0) then
        begin
          // Plugin list - show plugin icon
          if not FManager.Plugins[Node.Index].Valid then ImageIndex := STATE_IMG_WARN
          else ImageIndex := STATE_IMG_ACTIONS + FManager.Plugins[Node.Index].ImageIndex;
        end;
    end;
end;

procedure TAHMDesignerForm.vstThemeChange(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
  Data, ParentData: PElementData;
  Item: TAHMUIObject;
  List: TAHMUIObjectList;
begin
  // Ignore this event when exiting or updating tree focus node
  if Application.Terminated or FUpdatingTree then Exit;

  if Node <> nil then
  begin
    Data := Sender.GetNodeData(Node);
    ParentData := Sender.GetNodeData(Node.Parent);

    // Update selection
    case Sender.GetNodeLevel(Node) of
      1: if Data.Category = etDescriptors then
         begin
           // Load selected descriptor
           with FManager do SelectedDescriptor := ThemeModules[Node.Index];
           RefreshDescriptor(FManager.CurrentObjectInvalid);
         end
         else if Data.Category = etPlugin then
         begin
           // Select plugin
           with FManager do
           begin
             SelectedPlugin := Plugins[Node.Index];
             DisplayNodeProperties(SelectedPlugin);
           end;
         end;
      2: begin
           // Load selected item in properties view
           if (ParentData <> nil) and Assigned(ParentData.Action) then
           begin
             // Base/Custom indicator for descriptor item
             List := FManager.ObjectList[TAHMUIObjectType(ParentData.Action.Tag)];
             if Assigned(List) then
             begin
               Item := TAHMUIObject(List.Objects[Node.Index]);
               DisplayNodeProperties(Item);
             end;
           end;
         end;
    end;
  end;
end;

procedure TAHMDesignerForm.vstThemeChecked(Sender: TBaseVirtualTree;
  Node: PVirtualNode);
var
  Data: PElementData;
begin
  Data := Sender.GetNodeData(Node);
  FThemeTree.Selected[Node] := True;
  FThemeTree.FocusedNode := Node;

  // Validate that this is a descriptor node
  if (Sender.GetNodeLevel(Node) = 1) and (Data.Category = etDescriptors) then
  begin
    // Load selected descriptor
    with FManager do SelectedDescriptor := ThemeModules[Node.Index];
    RefreshDescriptor(FManager.CurrentObjectInvalid);
  end;
end;

procedure TAHMDesignerForm.vstThemeContextPopup(Sender: TObject;
  MousePos: TPoint; var Handled: Boolean);
var
  Popup: TPopupMenu;
begin
  // Determine appropriate Popup menu for selected node
  case GetSelectedCategory of
    etDescriptors: Popup := popDescriptors;
    etVisual, etInterface, etAudio, etText: Popup := popTreeObjects;
    etPlugin: Popup := popPlugins;
    else Popup := nil;
  end;

  if Assigned(Popup) then
  begin
    MousePos := TControl(Sender).ClientToScreen(MousePos);
    Popup.Popup(MousePos.X, MousePos.Y);
    Handled := True;
  end;
end;

procedure TAHMDesignerForm.vstThemeStartDrag(Sender: TObject; var DragObject: TDragObject);
begin
  // Use custom drag object to render dragged item
  FDragRenderer := TAHMDragRenderer.Create(FThemeTree);
  FDragRenderer.UIObject := FDragObject;
  FDragRenderer.Images := FLargeTreeImages;

  DragObject := FDragRenderer;
  FDraggingTree := True;
end;

procedure TAHMDesignerForm.vstThemeDragAllowed(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex; var Allowed: Boolean);
begin
  FDragObject := GetSelectedObject(Node);

  // Check whether this is a supported object type for dragging
  Allowed := FManager.DragObjectEnabled[FDragObject];
end;

procedure TAHMDesignerForm.vstThemeEndDrag(Sender, Target: TObject; X, Y: Integer);
begin
  FreeAndNil(FDragRenderer);
  FDraggingTree := False;

  // Force a repaint of the treeview
  FThemeTree.Invalidate;
end;

procedure TAHMDesignerForm.vstThemeDblClick(Sender: TObject);
begin
  // Call default action of appropriate popup menu
  case GetSelectedCategory of
    etVisual, etInterface, etAudio, etText: acPreviewObject.Execute;
    etPlugin: acConfigurePlugin.Execute;
  end;
end;

//-----------------------------------------------------------------------------
//------------------------ Object Inspector Events ----------------------------
//-----------------------------------------------------------------------------

procedure TAHMDesignerForm.InspectorItemDoubleClicked(Sender: TObject;
  Item: TJvCustomInspectorItem);
begin
  // Jump to double clicked UIObject
  UpdateSelection(FManager.ItemDoubleClicked(Item));
end;

procedure TAHMDesignerForm.InspectorItemSelected(Sender: TObject);
begin
  // Display hint for selected inspector property
  UpdateStatus(FManager.ItemHint(FInspector.Selected));
end;

procedure TAHMDesignerForm.InspectorItemValueChanged(Sender: TObject;
  Item: TJvCustomInspectorItem);
var
  Target: TAHMUIObject;
begin
  Target := FManager.ItemModified(Item);
  if Assigned(Target) then
  begin
    // Force repaint of this item in treeview
    FThemeTree.Invalidate;

    // Update form caption in case name is changed
    UpdateFormCaption(Target);

    // Update XML representation of object
    UpdateXML(Target);
  end;
end;

procedure TAHMDesignerForm.InspectorItemValueChanging(Sender: TObject;
  Item: TJvCustomInspectorItem; var NewValue: string; var AllowChange: Boolean);
begin
  // Notify designer manager of forthcoming change
  FManager.ItemModifying(Item);
end;

//-----------------------------------------------------------------------------
//----------------------------- Menu Events -----------------------------------
//-----------------------------------------------------------------------------

procedure TAHMDesignerForm.miCustomActionClick(Sender: TObject);
var
  UIObject: TAHMUIObject;
begin
  // Perform appropriate custom modify action for current UI object
  UIObject := GetSelectedObject;
  if Assigned(UIObject) and (Sender is TMenuItem) then
    UIObject.Modify(TMenuItem(Sender).Tag);
end;

procedure TAHMDesignerForm.miHistoryItemClick(Sender: TObject);
begin
  // Suppress navigation events whilst selecting item
  FNavigatingTree := True;
  try
    with Sender as TMenuItem do
    begin
      FManager.Navigation.Position := Tag - 1;
      Checked := True;
    end;
    UpdateSelection(FManager.Navigation.Current);
  finally
    FNavigatingTree := False;
  end;
end;

//-----------------------------------------------------------------------------
//---------------------------- Action Events ----------------------------------
//-----------------------------------------------------------------------------

procedure TAHMDesignerForm.acNewExecute(Sender: TObject);
begin
  if FManager.NewTheme then
    InitialiseTheme;
end;

procedure TAHMDesignerForm.acOpenExecute(Sender: TObject);
begin
  if FManager.OpenTheme('') then
    InitialiseTheme;
end;

procedure TAHMDesignerForm.acCloseUpdate(Sender: TObject);
begin
  acClose.Enabled := FManager.ShutdownEnabled;
end;

procedure TAHMDesignerForm.acCloseExecute(Sender: TObject);
begin
  // Close mediate
  FManager.ShutDownMediate;
end;

procedure TAHMDesignerForm.acOptionsExecute(Sender: TObject);
begin
  // Configure designer preference options
  if FManager.ConfigurePreferences then
    ApplyPreferences;
end;

procedure TAHMDesignerForm.acEditExecute(Sender: TObject);
var
  UIObject: TAHMUIObject;
begin
  UIObject := GetSelectedObject;

  if Assigned(UIObject) and UIObject.Modify then
  begin
    // Object is modified, refresh treeview and object inspector
    FThemeTree.Invalidate;
    FInspector.Invalidate;
  end;
end;

procedure TAHMDesignerForm.acEditUpdate(Sender: TObject);
var
  UIObject: TAHMUIObject;
begin
  UIObject := GetSelectedObject;
  if Assigned(UIObject) and (UIObject.ModifyActions > 0) then
  begin
    acEdit.Enabled := True;
    acEdit.Caption := UIObject.ModifyCaption;
  end
  else
  begin
    acEdit.Enabled := False;
    acEdit.Caption := 'Edit Object';
  end;
end;

procedure TAHMDesignerForm.acErrorsExecute(Sender: TObject);
begin
  FManager.Errors.DisplayErrors;
end;

procedure TAHMDesignerForm.acErrorsUpdate(Sender: TObject);
begin
  acErrors.Enabled := FManager.Errors.DisplayAvailable;
end;

procedure TAHMDesignerForm.acSaveAsExecute(Sender: TObject);
begin
  if FManager.SaveThemeAs then
    RefreshDescriptor;
end;

procedure TAHMDesignerForm.acSaveAsUpdate(Sender: TObject);
begin
  acSaveAs.Enabled := FManager.SaveEnabled;
end;

procedure TAHMDesignerForm.acSaveExecute(Sender: TObject);
begin
  if FManager.SaveTheme then
    RefreshDescriptor;
end;

procedure TAHMDesignerForm.acSaveUpdate(Sender: TObject);
begin
  acSave.Enabled := FManager.SaveEnabled;
end;

procedure TAHMDesignerForm.acExitExecute(Sender: TObject);
begin
  if FManager.ExitDesigner then
  begin
    // Important - we need to clear the inspector before we exit
    FInspector.Clear;

    // Quit the application
    Application.Terminate;
  end;
end;

procedure TAHMDesignerForm.acExportThemeExecute(Sender: TObject);
begin
  // Export current theme
  FManager.ExportTheme;
end;

procedure TAHMDesignerForm.acExportThemeUpdate(Sender: TObject);
begin
  acExportTheme.Enabled := FManager.SaveEnabled;
end;

procedure TAHMDesignerForm.acApplyExecute(Sender: TObject);
begin
  // Apply changes to XML
  if FManager.ApplyChanges then
  begin
    // Revert will refresh display to new XML
    acRevert.Execute;
  end
  else
    ShowMessage(FManager.ErrorMessage);
end;

procedure TAHMDesignerForm.acBackExecute(Sender: TObject);
begin
  // Suppress navigation events whilst moving backward
  FNavigatingTree := True;
  try
    UpdateSelection(FManager.Navigation.Previous);
  finally
    FNavigatingTree := False;
  end;
end;

procedure TAHMDesignerForm.acBackUpdate(Sender: TObject);
begin
  acBack.Enabled := FManager.Navigation.PreviousAvailable;
end;

procedure TAHMDesignerForm.acFindAgainExecute(Sender: TObject);
begin
  FindSelection(FManager.FindText);
end;

procedure TAHMDesignerForm.acFindAgainUpdate(Sender: TObject);
begin
  acFindAgain.Enabled := FManager.FindAgainEnabled;
end;

procedure TAHMDesignerForm.acFindObjectExecute(Sender: TObject);
begin
  if FManager.FindObject then FindSelection(FManager.FindText);
end;

procedure TAHMDesignerForm.acFindObjectUpdate(Sender: TObject);
begin
  acFindObject.Enabled := FManager.FindObjectEnabled;
end;

procedure TAHMDesignerForm.acForwardExecute(Sender: TObject);
begin
  // Suppress navigation events whilst moving forward
  FNavigatingTree := True;
  try
    UpdateSelection(FManager.Navigation.Next);
  finally
    FNavigatingTree := False;
  end;
end;

procedure TAHMDesignerForm.acForwardUpdate(Sender: TObject);
begin
  acForward.Enabled := FManager.Navigation.NextAvailable;
end;

procedure TAHMDesignerForm.acBaseDescriptorExecute(Sender: TObject);
begin
  // Toggle between base/custom descriptors
  if FManager.SelectedDescriptor = BASE_DESCRIPTOR_PREFIX then
    FManager.SelectedDescriptor := FManager.CustomDescriptor
  else
    FManager.SelectedDescriptor := BASE_DESCRIPTOR_PREFIX;

  RefreshDescriptor;
end;

procedure TAHMDesignerForm.acBaseDescriptorUpdate(Sender: TObject);
begin
  // Update action state and hint according to selection
  if FManager.CustomDescriptor <> '' then
  begin
    if FManager.SelectedDescriptor = BASE_DESCRIPTOR_PREFIX then
    begin
      acBaseDescriptor.Enabled := True;
      acBaseDescriptor.Checked := True;
      acBaseDescriptor.Hint := HINT_SELECT_CUSTOM
    end
    else
    begin
      acBaseDescriptor.Enabled := True;
      acBaseDescriptor.Checked := False;
      acBaseDescriptor.Hint := HINT_SELECT_BASE;
    end;
  end
  else
  begin
    acBaseDescriptor.Enabled := False;
    acBaseDescriptor.Checked := False;
    acBaseDescriptor.Hint := '';
  end;
end;

procedure TAHMDesignerForm.acCopyExecute(Sender: TObject);
begin
  // Perform appropriate copy operation to clipboard
  if GetSelectedCategory = etDescriptors then
    FManager.CopyDescriptor(GetSelectedDescriptor)
  else
    FManager.CopyObject(GetSelectedObject);
end;

procedure TAHMDesignerForm.acCopyUpdate(Sender: TObject);
begin
  // Update action state - must have a UIObject or descriptor selected
  if GetSelectedCategory = etDescriptors then
    acCopy.Enabled := FThemeTree.Focused and FManager.CopyDescriptorEnabled[GetSelectedDescriptor]
  else
    acCopy.Enabled := FThemeTree.Focused and FManager.CopyObjectEnabled[GetSelectedObject];
end;

procedure TAHMDesignerForm.acCutExecute(Sender: TObject);
begin
  // Cut selected UIObject
  FManager.CutObject(GetSelectedObject);
end;

procedure TAHMDesignerForm.acCutUpdate(Sender: TObject);
begin
  // Update action state - must have a UIObject selected
  acCut.Enabled := FThemeTree.Focused and (GetSelectedObject <> nil);
end;

procedure TAHMDesignerForm.acPasteExecute(Sender: TObject);
var
  NewObject: TAHMUIObject;
begin
  // Paste operation depends on type of node selected
  if GetSelectedCategory = etDescriptors then
  begin
    if FManager.PasteDescriptor then
    begin
      RefreshDescriptor(FManager.CurrentObjectInvalid);
      UpdateSelection(FManager.SelectedDescriptor);
    end;
  end
  else
  begin
    NewObject := FManager.PasteObject;
    UpdateSelection(NewObject, True);
  end;
end;

procedure TAHMDesignerForm.acPasteUpdate(Sender: TObject);
begin
  // Paste is possible if the clipboard contents are compatible
  case GetSelectedCategory of
    etNone: acPaste.Enabled := False;
    etDescriptors: acPaste.Enabled := FThemeTree.Focused and
                                      (FManager.CopiedDescriptor <> '');
    else acPaste.Enabled := FThemeTree.Focused and
                            (FManager.CopiedObject <> nil) and
                            (FManager.CopiedObject.ObjectType = GetSelectedType);
  end;
end;

procedure TAHMDesignerForm.acPreviewObjectExecute(Sender: TObject);
begin
  UpdateRenderer(GetSelectedObject);
end;

procedure TAHMDesignerForm.acPreviewObjectUpdate(Sender: TObject);
begin
  acPreviewObject.Enabled := FManager.PreviewObjectEnabled[GetSelectedObject];
end;

procedure TAHMDesignerForm.acBordersExecute(Sender: TObject);
begin
  FManager.DrawBorders := acBorders.Checked;
end;

procedure TAHMDesignerForm.acRedoExecute(Sender: TObject);
begin
  if FManager.Redo(FInspector) then ObjectChanged(nil);
end;

procedure TAHMDesignerForm.acRedoUpdate(Sender: TObject);
begin
  acRedo.Enabled := FManager.History.RedoAvailable;
end;

procedure TAHMDesignerForm.acRemoveSelectionExecute(Sender: TObject);
begin
  FManager.RemoveSelection;
end;

procedure TAHMDesignerForm.acRenameObjectExecute(Sender: TObject);
begin
  // Rename the selected object or descriptor & clear object inspector
  if GetSelectedCategory = etDescriptors then
  begin
    if FManager.RenameDescriptor(GetSelectedDescriptor) then
      RefreshDescriptor;
  end
  else if FManager.RenameObject(GetSelectedObject) then
    UpdateSelection(GetSelectedObject, True);
end;

procedure TAHMDesignerForm.acRenameObjectUpdate(Sender: TObject);
begin
  // Update action state - must have a UIObject or descriptor selected
  if GetSelectedCategory = etDescriptors then
    acRenameObject.Enabled := FManager.RenameDescriptorEnabled[GetSelectedDescriptor]
  else
    acRenameObject.Enabled := FManager.RenameObjectEnabled[GetSelectedObject];
end;

procedure TAHMDesignerForm.acRestartExecute(Sender: TObject);
begin
  // Restart mediate
  FManager.RestartMediate;
end;

procedure TAHMDesignerForm.acRestartUpdate(Sender: TObject);
begin
  acRestart.Enabled := FManager.RestartEnabled;
end;

procedure TAHMDesignerForm.acRevertExecute(Sender: TObject);
begin
  if Assigned(FManager.CurrentObject) then
  begin
    // Undo modifications to XML
    FXMLEditor.Lines.Clear;
    FManager.CurrentObject.SaveAsXML(FXMLEditor.Lines);
    FManager.CurrentObject.XMLDraft := '';

    FXMLEditor.SyntaxHighlighting := False;
    acApply.Enabled := False;
  end;
end;

procedure TAHMDesignerForm.acRevertObjectExecute(Sender: TObject);
begin
  FManager.RevertObject(GetSelectedObject);
end;

procedure TAHMDesignerForm.acRevertObjectUpdate(Sender: TObject);
begin
  acRevertObject.Enabled := FManager.RevertObjectEnabled[GetSelectedObject];
end;

procedure TAHMDesignerForm.acLaunchExecute(Sender: TObject);
begin
  // Initialise mediate
  FManager.LaunchMediate;
end;

procedure TAHMDesignerForm.acLaunchUpdate(Sender: TObject);
begin
  acLaunch.Enabled := FManager.LaunchEnabled;
end;

procedure TAHMDesignerForm.acLoadPluginExecute(Sender: TObject);
begin
  if not FManager.SelectedPlugin.Load then
    TaskMsgDialog('Load Plugin', 'Failed to load plugin', 'The selected ' +
                  'mediate plugin could not be loaded.', mtWarning);
end;

procedure TAHMDesignerForm.acLoadPluginUpdate(Sender: TObject);
begin
  acLoadPlugin.Enabled := IsPluginSelected and not FManager.SelectedPlugin.Loaded;
end;

procedure TAHMDesignerForm.acMoveObjectExecute(Sender: TObject);
var
  BaseObject: TAHMUIObject;
begin
  BaseObject := GetSelectedObject;

  // Move the currently selected object and refresh descriptor
  if FManager.MoveBaseObject(BaseObject) then
  begin
    RefreshDescriptor;
    UpdateSelection(BaseObject);
  end;
end;

procedure TAHMDesignerForm.acMoveObjectUpdate(Sender: TObject);
var
  BaseObject: TAHMUIObject;
begin
  BaseObject := GetSelectedObject;

  // Update action state and hint according to selection
  if (BaseObject <> nil) and (FManager.CustomDescriptor <> '') then
  begin
    if BaseObject.IsBaseObject then
    begin
      acMoveObject.Enabled := True;
      acMoveObject.Hint := HINT_MOVE_TO_CUSTOM
    end
    else
    begin
      acMoveObject.Enabled := True;
      acMoveObject.Hint := HINT_MOVE_TO_BASE;
    end;
  end
  else
  begin
    acMoveObject.Enabled := False;
    acMoveObject.Hint := '';
  end;
end;

procedure TAHMDesignerForm.acShutdownExecute(Sender: TObject);
begin
  // Close mediate
  FManager.ShutDownMediate;
end;

procedure TAHMDesignerForm.acShutdownUpdate(Sender: TObject);
begin
  acShutdown.Enabled := FManager.ShutdownEnabled;
end;

procedure TAHMDesignerForm.acStartPluginExecute(Sender: TObject);
begin
  if not FManager.SelectedPlugin.Start then
    TaskMsgDialog('Start Plugin', 'Failed to start plugin', 'The selected ' +
                  'mediate plugin could not be started.', mtWarning);
end;

procedure TAHMDesignerForm.acStartPluginUpdate(Sender: TObject);
begin
  acStartPlugin.Enabled := IsPluginSelected and FManager.SelectedPlugin.Loaded and
                           not FManager.SelectedPlugin.Started;
end;

procedure TAHMDesignerForm.acStopPluginExecute(Sender: TObject);
begin
  if not FManager.SelectedPlugin.Stop then
    TaskMsgDialog('Stop Plugin', 'Failed to stop plugin', 'The selected ' +
                  'mediate plugin could not be stopped.', mtWarning);
end;

procedure TAHMDesignerForm.acStopPluginUpdate(Sender: TObject);
begin
  acStopPlugin.Enabled := IsPluginSelected and FManager.SelectedPlugin.Loaded and
                          FManager.SelectedPlugin.Started;
end;

procedure TAHMDesignerForm.acNewObjectExecute(Sender: TObject);
var
  Ancestor, NewObject: TAHMUIObject;
begin
  // Create a new descriptor or theme object as appropriate
  if GetSelectedCategory = etDescriptors then
  begin
    if FManager.AddDescriptor then
    begin
      RefreshDescriptor(FManager.CurrentObjectInvalid);
      UpdateSelection(FManager.SelectedDescriptor);
    end;
  end
  else
  begin
    Ancestor := GetSelectedObject;
    NewObject := FManager.AddObject(Ancestor, GetSelectedType);

    // If a new object was created then we need to refresh tree and display it
    if Assigned(NewObject) then
      UpdateSelection(NewObject, True);
  end;
end;

procedure TAHMDesignerForm.acNewObjectUpdate(Sender: TObject);
begin
  // We must have a theme list or theme object selected to use wizard
  acNewObject.Enabled := GetSelectedCategory in [etDescriptors, etVisual,
                                                 etInterface, etAudio, etText];
end;

procedure TAHMDesignerForm.acNewScreenExecute(Sender: TObject);
begin
  // Add and switch display to new screen
  FManager.AddScreen;
end;

procedure TAHMDesignerForm.acThemePropertiesExecute(Sender: TObject);
begin
  // Display theme properties dialog
  FManager.EditTheme;
end;

procedure TAHMDesignerForm.acThemePropertiesUpdate(Sender: TObject);
begin
  acThemeProperties.Enabled := FManager.ThemeLoaded;
end;

procedure TAHMDesignerForm.acToolSelection(Sender: TObject);
begin
  // Choose mediate tool
  with Sender as TAction do
    FManager.CurrentTool := TAHMToolType(Tag);
end;

procedure TAHMDesignerForm.acToolUpdate(Sender: TObject);
begin
  with Sender as TAction do
    Enabled := FManager.ToolEnabled[TAHMToolType(Tag)];
end;

procedure TAHMDesignerForm.acUndoExecute(Sender: TObject);
begin
  if FManager.Undo(FInspector) then ObjectChanged(nil);
end;

procedure TAHMDesignerForm.acUndoUpdate(Sender: TObject);
begin
  acUndo.Enabled := FManager.History.UndoAvailable;
end;

procedure TAHMDesignerForm.acUnloadPluginExecute(Sender: TObject);
begin
  if not FManager.SelectedPlugin.Unload then
    TaskMsgDialog('Unload Plugin', 'Failed to unload plugin', 'The selected ' +
                  'mediate plugin could not be unloaded.', mtWarning);
end;

procedure TAHMDesignerForm.acUnloadPluginUpdate(Sender: TObject);
begin
  acUnloadPlugin.Enabled := IsPluginSelected and FManager.SelectedPlugin.Loaded;
end;

procedure TAHMDesignerForm.acUpdatePluginExecute(Sender: TObject);
begin
  if not FManager.SelectedPlugin.Update then
    TaskMsgDialog('Update Plugin', 'Failed to update plugin', 'The selected ' +
                  'mediate plugin could not be updated.', mtWarning);
end;

procedure TAHMDesignerForm.acUpdatePluginUpdate(Sender: TObject);
begin
  acUpdatePlugin.Enabled := IsPluginSelected and FManager.SelectedPlugin.Loaded;
end;

procedure TAHMDesignerForm.acZoomFullExecute(Sender: TObject);
begin
  ScreenInstance.ZoomReset;
end;

procedure TAHMDesignerForm.acZoomInExecute(Sender: TObject);
begin
  ScreenInstance.ZoomIn;
end;

procedure TAHMDesignerForm.acZoomOutExecute(Sender: TObject);
begin
  ScreenInstance.ZoomOut;
end;

procedure TAHMDesignerForm.acCloseTabExecute(Sender: TObject);
var
  Current: Integer;
begin
  // Delete current tab and rebuild preview list
  Current := tcRenderer.TabIndex;
  FManager.Previews.DeleteObject(GetSelectedPreview);

  // Highlight adjacent tab
  if Current < tcRenderer.Tabs.Count then tcRenderer.TabIndex := Current
                                     else tcRenderer.TabIndex := Pred(Current);

  // Inform designer of the object we want to render
  FManager.PreviewObject := GetSelectedPreview;

  // Update controls for selected preview
  UpdatePreviewControls;
end;

procedure TAHMDesignerForm.acCloseTabUpdate(Sender: TObject);
begin
  acCloseTab.Enabled := tcRenderer.TabIndex > 0;
end;

procedure TAHMDesignerForm.acConfigureExecute(Sender: TObject);
begin
  // Display mediate advanced settings dialog
  ApplicationManagerInstance.ConfigureSettings;
end;

procedure TAHMDesignerForm.acConfigurePluginExecute(Sender: TObject);
begin
  if not FManager.SelectedPlugin.Configure then
    TaskMsgDialog('Configure Plugin', 'Failed to configure plugin', 'This ' +
                  'plugin does not have any configurable properties.', mtInformation);
end;

procedure TAHMDesignerForm.acConfigurePluginUpdate(Sender: TObject);
begin
  acConfigurePlugin.Enabled := IsPluginSelected and FManager.SelectedPlugin.Configurable;
end;

procedure TAHMDesignerForm.acDeleteExecute(Sender: TObject);
begin
  // Delete the selected object or descriptor & clear object inspector
  if GetSelectedCategory = etDescriptors then
  begin
    if FManager.DeleteDescriptor(GetSelectedDescriptor) then
    begin
      RefreshDescriptor(FManager.CurrentObjectInvalid);
      UpdateSelection(FManager.SelectedDescriptor);
    end;
  end
  else if FManager.DeleteObject(GetSelectedObject) then
    RefreshDescriptor(True);
end;

procedure TAHMDesignerForm.acDeleteUpdate(Sender: TObject);
begin
  // Update action state - must have a UIObject or descriptor selected
  if GetSelectedCategory = etDescriptors then
    acDelete.Enabled := FManager.DeleteDescriptorEnabled[GetSelectedDescriptor]
  else
    acDelete.Enabled := FManager.DeleteObjectEnabled[GetSelectedObject];
end;

procedure TAHMDesignerForm.acAboutExecute(Sender: TObject);
begin
  // Display mediate about dialog
  ApplicationManagerInstance.DisplayAbout;
end;

procedure TAHMDesignerForm.acHyperlinkExecute(Sender: TObject);
begin
  // Launch mediate support in browser
  DisplayBrowserSupport;
end;

procedure TAHMDesignerForm.acImportThemeExecute(Sender: TObject);
begin
  // Import a theme archive
  FManager.ImportTheme;
end;

//-----------------------------------------------------------------------------
//-------------------------- mediate Preview Events ---------------------------
//-----------------------------------------------------------------------------

procedure TAHMDesignerForm.tcRendererChange(Sender: TObject);
begin
  // Inform designer of the object we want to render
  FManager.PreviewObject := GetSelectedPreview;

  // Update controls for selected preview
  UpdatePreviewControls;
end;

procedure TAHMDesignerForm.pbHeaderPaint(Sender: TObject);
var
  i: Integer;
begin
  with pbHeader do
  begin
    Canvas.Brush.Color := clWhite;
    Canvas.FillRect(GetClientRect);

    // Draw icon for selected control/object
    if FManager.CurrentObject is TAHMControl then
      i := FManager.CurrentObject.ImageIndex + STATE_IMG_OBJECTS
    else if FManager.CurrentObject is TAHMUIObject then
      i := FManager.CurrentObject.ImageIndex + STATE_IMG_ACTIONS
    else Exit;
    FLargeTreeImages.Draw(Canvas, 2, 2, i);

    // Draw icon for base/custom descriptor
    if FManager.CurrentObject.IsBaseObject then i := 0 else i := 1;
    FLargeTreeImages.Draw(Canvas, pbHeader.Width - 34, 2, i);

    Canvas.Font.Style := [fsBold];
    Canvas.TextOut(38, 4, FManager.CurrentObject.Name);
    Canvas.Font.Style := [];
    Canvas.TextOut(38, 18, FManager.CurrentObject.ClassName + ' properties:');
  end;
end;

procedure TAHMDesignerForm.pgcPreviewTypeChange(Sender: TObject);
begin
  // Ensure selected tab is updated
  if pgcPreviewType.ActivePage = tsConsole then
  begin
    mmConsoleLogChange(nil);
    ecConsoleCmd.Items := ApplicationManagerInstance.UIManager.DebugConsole.CommandHistory;
    ecConsoleCmd.SetFocus;
  end;
end;

procedure TAHMDesignerForm.pnMediatePreviewDblClick(Sender: TObject);
begin
  acEdit.Execute;
end;

procedure TAHMDesignerForm.pnMediatePreviewDragDrop(Sender, Source: TObject; X,
  Y: Integer);
begin
  FManager.ProcessDragDrop(X, Y, FDragObject);
end;

procedure TAHMDesignerForm.pnMediatePreviewDragOver(Sender, Source: TObject; X,
  Y: Integer; State: TDragState; var Accept: Boolean);
begin
  Accept := IsDragObject(Source) and FManager.ProcessDragOver(X, Y, FDragObject);
end;

procedure TAHMDesignerForm.pnMediatePreviewMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  // Pass mouse events to designer manager to handle
  if ssLeft in Shift then
    FManager.ProcessMouseDown(X, Y, Shift)
  else if ssRight in Shift then
    FManager.ProcessMouseMenu;
end;

procedure TAHMDesignerForm.pnMediatePreviewMouseEnter(Sender: TObject);
begin
  // Enable mediate keyboard support
  FPreviewSelected := True;
end;

procedure TAHMDesignerForm.pnMediatePreviewMouseLeave(Sender: TObject);
begin
  // Disable mediate keyboard support
  FPreviewSelected := False;
end;

procedure TAHMDesignerForm.pnMediatePreviewMouseMove(Sender: TObject;
  Shift: TShiftState; X, Y: Integer);
begin
  // Pass to designer manager
  FManager.ProcessMouseMove(X, Y, ssLeft in Shift);
end;

procedure TAHMDesignerForm.pnMediatePreviewMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  // Check that left mouse button was released then pass to designer manager
  if Button = mbLeft then FManager.ProcessMouseUp(X, Y);
end;

procedure TAHMDesignerForm.pnMediateFrameResize(Sender: TObject);
const
  XMARGIN = 1;
  YMARGIN = 2;
var
  MaxHeight, MaxWidth: Integer;
begin
  MaxWidth := pnMediateFrame.ClientWidth - (2 * XMARGIN);
  MaxHeight := pnMediateFrame.ClientHeight - (2 * YMARGIN);

  if FManager.Preferences.PreventPreviewStretch and
     (MaxWidth > ScreenInstance.PhysicalWidth) and
     (MaxHeight > ScreenInstance.PhysicalHeight) then
  begin
    // Clamp maximum size to prevent stretching if required
    pnMediatePreview.Width := ScreenInstance.PhysicalWidth;
    pnMediatePreview.Left := (MaxWidth - pnMediatePreview.Width) div 2 + XMARGIN;
    pnMediatePreview.Height := ScreenInstance.PhysicalHeight;
    pnMediatePreview.Top := (MaxHeight - pnMediatePreview.Height) div 2 + YMARGIN;
  end
  else
  begin
    // Correct aspect ratio for mediate preview display
    if (MaxWidth / ScreenInstance.AspectRatio) < MaxHeight then
    begin
      pnMediatePreview.Width := MaxWidth;
      pnMediatePreview.Left := XMARGIN;
      pnMediatePreview.Height := Round(MaxWidth / ScreenInstance.AspectRatio);
      pnMediatePreview.Top := (MaxHeight - pnMediatePreview.Height) div 2 + YMARGIN;
    end
    else
    begin
      pnMediatePreview.Height := MaxHeight;
      pnMediatePreview.Top := YMARGIN;
      pnMediatePreview.Width := Round(MaxHeight * ScreenInstance.AspectRatio);
      pnMediatePreview.Left := (MaxWidth - pnMediatePreview.Width) div 2 + XMARGIN;
    end;
  end;

  // Update preview window dimensions in screen
  ScreenInstance.DesignerWidth := pnMediatePreview.Width;
  ScreenInstance.DesignerHeight := pnMediatePreview.Height;
end;

//-----------------------------------------------------------------------------
//---------------------------- mediate XML Events -----------------------------
//-----------------------------------------------------------------------------

procedure TAHMDesignerForm.mmXMLChange(Sender: TObject);
begin
  if FModifyingXML then Exit;

  // Save any draft XML changes to current UIObject
  if Assigned(FManager.CurrentObject) then
  begin
    FManager.CurrentObject.XMLDraft := FXMLEditor.Lines.Text;
    FXMLEditor.SyntaxHighlighting := True;
    acApply.Enabled := True;
  end;
end;

procedure TAHMDesignerForm.mmXMLPaintGutter(Sender: TObject; Canvas: TCanvas);
var
  i: integer;
  Rect: TRect;
begin
  // Paint line numbers in XML editor gutter
  with Sender as TJvHLEditor do
    for i := TopRow to TopRow + VisibleRowCount do
    begin
      Rect := Bounds(2, (i - TopRow) * CellRect.Height, GutterWidth - 2 - 5, CellRect.Height);
      DrawText(Canvas.Handle, PChar(IntToStr(i + 1)), -1, Rect, DT_RIGHT or DT_VCENTER or DT_SINGLELINE);
    end;
end;

//-----------------------------------------------------------------------------
//-------------------------- mediate Console Events ---------------------------
//-----------------------------------------------------------------------------

procedure TAHMDesignerForm.ecConsoleCmdKeyPress(Sender: TObject; var Key: Char);
begin
  if Ord(Key) = 13 then
    if ApplicationManagerInstance.State = asRunning then
    begin
      // Pass entered commands to console
      ApplicationManagerInstance.UIManager.DebugConsole.ProcessCommand(ecConsoleCmd.Text);
      ecConsoleCmd.Items := ApplicationManagerInstance.UIManager.DebugConsole.CommandHistory;
      ecConsoleCmd.Text := '';
      Key := Chr(0); // Suppress sound effect
    end;
end;

procedure TAHMDesignerForm.mmConsoleLogChange(Sender: TObject);
begin
  // Ensure last line of memo text is visible
  with mmConsoleLog do
  begin
    SelStart := Length(Text);
    Perform(EM_SCROLLCARET, 0, 0);
  end;
end;

//-----------------------------------------------------------------------------
//-------------------------- DesignerManager Events ---------------------------
//-----------------------------------------------------------------------------

procedure TAHMDesignerForm.ObjectAdded(Added: TAHMUIObject);
begin
  // If a new object was created then we need to refresh tree and display it
  if Assigned(Added) then UpdateSelection(Added, True);
end;

procedure TAHMDesignerForm.ObjectChanged(Changed: TAHMUIObject);
begin
  // Refresh treeview with new object properties
  if GetSelectedObject <> Changed then UpdateSelection(Changed)
                                  else FThemeTree.Invalidate;

  // Repaint object inspector and header in case item is now modified
  FInspector.Invalidate;
  pbHeader.Invalidate;
end;

procedure TAHMDesignerForm.SelectionChanged(Selected: TAHMUIObject);
begin
  // Refresh selected item in designer
  UpdateSelection(Selected);
end;

procedure TAHMDesignerForm.PreviewChanged(Sender: TObject);
begin
  // Refresh renderer tab history
  UpdatePreviewTabs;
end;

procedure TAHMDesignerForm.NavigationChanged(Sender: TObject);
begin
  // Refresh history menu items
  UpdateHistoryMenu;
end;

procedure TAHMDesignerForm.DescriptorLoaded(Descriptor: String);
begin
  // Refresh treeview and clear old custom object
  RefreshDescriptor(FManager.CurrentObjectInvalid);

  // If we have a descriptor selected currently then update it
  if GetSelectedCategory = etDescriptors then
    UpdateSelection(Descriptor);
end;

procedure TAHMDesignerForm.DesignerToolSelected(Selection: TAHMToolType);
begin
  // Keep toolbar in sync with "sticky" designer tool changes
  case Selection of
    ttMouse: acMouseTool.Checked := True;
    ttContainer: acNewContainer.Checked := True;
    ttControl: acNewControl.Checked := True;
    ttEdit: acEditTool.Checked := True;
    ttRotate: acRotateTool.Checked := True;
    ttPan: acPanTool.Checked := True;
  end;

  // Update status bar for selected tool
  UpdateToolStatus;

  // Update controls for selected preview
  UpdatePreviewControls;
end;

procedure TAHMDesignerForm.DesignerLaunched(Sender: TObject);
begin
  // Update form caption and populate tree
  InitialiseTheme;
end;

procedure TAHMDesignerForm.DesignerRestarted(Sender: TObject);
begin
  // Update form caption and populate tree
  InitialiseTheme;
end;

procedure TAHMDesignerForm.DesignerShutdown(Sender: TObject);
begin
  // Refresh preview to clear last mediate frame
  pnMediatePreview.Invalidate;
  pbHeader.Invalidate;

  // Clear object inspector
  FInspector.Clear;

  // Clear treeview and form caption
  CloseTheme;

  // Clear custom menu items & XML view
  UpdateItemMenus(nil);
  UpdateHistoryMenu;
  UpdateXML(nil);

  // Clear toolbar status
  acMouseTool.Checked := True;
  tbMediateToolbar.Invalidate;
  UpdateStatus;

  // Clear renderer tab history
  UpdatePreviewTabs;
end;

//-----------------------------------------------------------------------------
//------------------------- Miscellaneous Events ------------------------------
//-----------------------------------------------------------------------------

procedure TAHMDesignerForm.CMDialogKey(var Msg: TWMKey);
begin
  // Inhibit standard handling of tab key so it sends a key event instead
  if FPreviewSelected  and (Msg.CharCode = VK_TAB) then
    Msg.CharCode := 0
  else
    inherited;
end;

procedure TAHMDesignerForm.WMSysCommand(var Msg: TWmSysCommand);
begin
  // Override standard delphi handling to get proper effects under Vista
  case (Msg.CmdType and $FFF0) of
    SC_MINIMIZE:
    begin
      ShowWindow(Handle, SW_MINIMIZE);
      Msg.Result := 0;
    end;
    SC_RESTORE:
    begin
      ShowWindow(Handle, SW_RESTORE);
      Msg.Result := 0;
    end;
  else
    inherited;
  end;
end;

//-----------------------------------------------------------------------------
//-------------------------- User Interface Update ----------------------------
//-----------------------------------------------------------------------------

function TAHMDesignerForm.GetSelectedDescriptor: String;
var
  Node: PVirtualNode;
  Data: PElementData;
begin
  Node := FThemeTree.GetFirstSelected;
  Result := '';

  // Check we have a descriptor child node selected
  if (FThemeTree.GetNodeLevel(Node) = 1) then
  begin
    Data := FThemeTree.GetNodeData(Node);
    if Data.Category = etDescriptors then
      Result := FManager.ThemeModules[Node.Index];
  end;
end;

function TAHMDesignerForm.GetSelectedObject: TAHMUIObject;
begin
  // Delegate to method below for selected node
  Result := GetSelectedObject(FThemeTree.GetFirstSelected);
end;

function TAHMDesignerForm.GetSelectedObject(Node: PVirtualNode): TAHMUIObject;
var
  ParentData: PElementData;
  ObjectType: TAHMUIObjectType;
  List: TAHMUIObjectList;
begin
  Result := nil;

  // Check we have a UIObject child node selected
  if (Node <> nil) and (FThemeTree.GetNodeLevel(Node) = 2) then
  begin
    ParentData := FThemeTree.GetNodeData(Node.Parent);

     // Lookup UIObject for this node
     if (ParentData <> nil) and Assigned(ParentData.Action) then
     begin
       ObjectType := TAHMUIObjectType(ParentData.Action.Tag);
       List := FManager.ObjectList[ObjectType];
       Result := TAHMUIObject(List.Objects[Node.Index]);
     end;
  end;
end;

function TAHMDesignerForm.GetSelectedType: TAHMUIObjectType;
var
  Node: PVirtualNode;
  Data, ParentData: PElementData;
begin
  Node := FThemeTree.GetFirstSelected;
  Result := otNone;

  // Look for a theme list or theme item node to determine type
  if Node <> nil then
  begin
    Data := FThemeTree.GetNodeData(Node);
    ParentData := FThemeTree.GetNodeData(Node.Parent);

    case FThemeTree.GetNodeLevel(Node) of
      1: if Data.Category in [etVisual, etInterface, etAudio, etText] then
         begin
           // This is a theme list node, so no child is selected
           Result := TAHMUIObjectType(Data.Action.Tag);
         end;
      2: if (ParentData <> nil) and Assigned(ParentData.Action) then
         begin
           // This is a theme item node, use parent for type
           Result := TAHMUIObjectType(ParentData.Action.Tag);
         end;
    end;
  end;
end;

function TAHMDesignerForm.GetSelectedCategory: TElementType;
var
  Node: PVirtualNode;
  Data: PElementData;
begin
  Node := FThemeTree.GetFirstSelected;
  Result := etNone;

  // Determine category for selected node - walk up to top level
  while (Node <> nil) and (FThemeTree.GetNodeLevel(Node) > 1) do
    Node := Node.Parent;

  // Check category of top level node
  if Node <> nil then
  begin
    Data := FThemeTree.GetNodeData(Node);
    if Data <> nil then
      Result := Data.Category;
  end;
end;

function TAHMDesignerForm.GetSelectedPreview: TAHMUIObject;
begin
  // Determine which preview object is selected
  with tcRenderer do
    if TabIndex < 0 then Result := nil
                    else Result := TAHMUIObject(Tabs.Objects[TabIndex]);
end;

function TAHMDesignerForm.GetSelectedHint(Node: PVirtualNode; var Item: TAHMUIObject): String;
var
  Data, ParentData: PElementData;
  List: TAHMUIObjectList;
begin
  // Set default hint - empty
  Result := '';
  Item := nil;

  Data := FThemeTree.GetNodeData(Node);
  ParentData := FThemeTree.GetNodeData(Node.Parent);

  case FThemeTree.GetNodeLevel(Node) of
    0: begin
         // Top level nodes - theme descriptor categories
         case Data.Category of
           etDescriptors: Result := HINT_DESCRIPTORS;
           etVisual:      Result := HINT_VISUALS;
           etInterface:   Result := HINT_INTERFACE;
           etText:        Result := HINT_LANGUAGES;
           etAudio:       Result := HINT_AUDIO;
           etPlugin:      Result := HINT_PLUGINS;
         end;
       end;
    1: if Assigned(Data.Action) then
       begin
         // Theme descriptor heading
         Result := Data.Action.Hint;
       end
       else if ParentData.Category = etPlugin then
       begin
         // Plugin list
         Result := FManager.Plugins[Node.Index].Comment;
         Item := FManager.Plugins[Node.Index];
       end
       else
       begin
         // Descriptor modules
         Result := FManager.ModuleComments[Node.Index];
       end;
    2: if Assigned(ParentData.Action) then
       begin
         // Descriptor items
         List := FManager.ObjectList[TAHMUIObjectType(ParentData.Action.Tag)];
         if Assigned(List) then
         begin
           Item := TAHMUIObject(List.Objects[Node.Index]);
           if Assigned(Item) then Result := FManager.ItemHint(Item);
         end;
       end;
  end;
end;

function TAHMDesignerForm.IsPluginSelected: Boolean;
var
  Node: PVirtualNode;
  Data: PElementData;
begin
  Node := FThemeTree.GetFirstSelected;
  Result := False;

  // Check we have a plugin child node selected
  if Assigned(FManager.SelectedPlugin) and (FThemeTree.GetNodeLevel(Node) = 1) then
  begin
    Data := FThemeTree.GetNodeData(Node);
    Result := (Data.Category = etPlugin);
  end;
end;

procedure TAHMDesignerForm.UpdateToolStatus;
begin
  // Display appropriate help for tool in status bar
  case FManager.CurrentTool of
    ttMouse: UpdateStatus(STATUS_TOOL_MOUSE);
    ttContainer: UpdateStatus(STATUS_TOOL_CNTNR);
    ttControl: UpdateStatus(STATUS_TOOL_CNTRL);
    ttScreen: UpdateStatus(STATUS_TOOL_SCREEN);
    ttEdit: UpdateStatus(STATUS_TOOL_EDIT);
    ttPan: UpdateStatus(STATUS_TOOL_PAN);
    ttRotate: UpdateStatus(STATUS_TOOL_ROTATE);
    else UpdateStatus;
  end;
end;

procedure TAHMDesignerForm.UpdateMouseStatus;
begin
  sbBottomStatus.Panels[0].Text := FManager.MouseCoordinates;
  sbBottomStatus.Panels[1].Text := FManager.MouseSizeInfo;
end;

procedure TAHMDesignerForm.UpdateStatus(const Status: String = '');
begin
  sbBottomStatus.Panels[2].Text := Status;
end;

procedure TAHMDesignerForm.UpdateRenderer(Selection: TAHMUIObject);
begin
  // Inform designer of the object we want to render & update preview history
  FManager.PreviewObject := Selection;
  FManager.Previews.PreviewObject(Selection);

  // Select mediate tab or most recent preview
  if Selection = nil then tcRenderer.TabIndex := 0
                     else tcRenderer.TabIndex := 1;

  // Update controls for selected preview
  UpdatePreviewControls;
end;

procedure TAHMDesignerForm.UpdatePreviewControls;
var
  Selection: TAHMUIObject;
begin
  Selection := GetSelectedPreview;

  // Enable popup menu for preview tabs
  if (Selection = nil) and (FManager.CurrentTool = ttMouse) then
    tcRenderer.PopupMenu := nil
  else
    tcRenderer.PopupMenu := popPreview;
end;

procedure TAHMDesignerForm.InitialiseTheme;
begin
  // Update form caption to reflect theme name
  UpdateFormCaption;

  // Update tool description in status bar
  UpdateToolStatus;

  // Update controls for selected preview
  UpdatePreviewControls;

  // Rebuild the treeview from scratch
  FThemeTree.RootNodeCount := ROOT_NODE_COUNT;

  // Force refresh of mediate panel size calculation
  pnMediateFrameResize(pnMediateFrame);
end;

procedure TAHMDesignerForm.CloseTheme;
begin
  // Update form caption to reflect theme closed
  FCaptionBase := CAPTION_PREFIX + CAPTION_NO_THEME;

  // Remove all entries from string tree
  FThemeTree.RootNodeCount := 0;
end;

procedure TAHMDesignerForm.UpdateCaption;
begin
  // Update form caption to reflect modified status of theme
  if FManager.Modified then
    Caption := FCaptionBase + ' *'
  else
    Caption := FCaptionBase;
end;

procedure TAHMDesignerForm.UpdateFormCaption(Item: TAHMUIObject = nil);
begin
  // Update form caption to reflect theme name and selected object
  with FManager do
    if ThemeName <> '' then
    begin
      FCaptionBase := CAPTION_PREFIX + ThemeName + ' (' + SelectedDescriptor + ')';
      if Assigned(Item) then
        FCaptionBase := FCaptionBase +  ' - ' + Item.ClassName + ' ' + Item.Name;
    end
    else
      FCaptionBase := CAPTION_PREFIX + CAPTION_NO_THEME;

  // Refresh header image/caption
  pbHeader.Invalidate;
end;

procedure TAHMDesignerForm.UpdateCursor;
begin
  // Update cursor to reflect selected grab handle in designer
  case FManager.GrabHandle of
    ghInsert: pnMediatePreview.Cursor := crCross;
    ghMove: pnMediatePreview.Cursor := crSizeAll;
    ghPan: pnMediatePreview.Cursor := crHandPoint;
    ghTop, ghBottom: pnMediatePreview.Cursor := crSizeNS;
    ghLeft, ghRight: pnMediatePreview.Cursor := crSizeWE;
    ghTopLeft, ghBottomRight: pnMediatePreview.Cursor := crSizeNWSE;
    ghTopRight, ghBottomLeft: pnMediatePreview.Cursor := crSizeNESW;
    else pnMediatePreview.Cursor := crDefault;
  end;
end;

procedure TAHMDesignerForm.DisplayNodeProperties(Item: TAHMUIObject);
begin
  if Assigned(Item) then
  begin
    FInspector.Clear;
    AddInspectorUIObject(FInspector, Item);

    // Add new item to navigation history
    if not FNavigatingTree then FManager.Navigation.Visited(Item);

    // Display XML content for selected item
    UpdateXML(Item);
  end;

  // Update menu items for selected item
  UpdateItemMenus(Item);

  // Update form caption to reflect selection
  UpdateFormCaption(Item);
end;

procedure TAHMDesignerForm.UpdateXML(Item: TAHMUIObject);
begin
  // Update new current UIObject
  FManager.CurrentObject := Item;

  FModifyingXML := True;
  FXMLEditor.Lines.BeginUpdate;
  try
    if Assigned(Item) then
    begin
      // Display XML content for new item
      if Item.XMLDraft <> '' then
      begin
        // Redisplay cached draft XML
        FXMLEditor.Lines.Text := Item.XMLDraft;
        FXMLEditor.SyntaxHighlighting := True;
        acApply.Enabled := True;
      end
      else
      begin
        // Populate unmodified XML
        FXMLEditor.Lines.Clear;
        Item.SaveAsXML(FXMLEditor.Lines);
        FXMLEditor.SyntaxHighlighting := False;
        acApply.Enabled := False;
      end;

      // Enable updates
      FXMLEditor.ReadOnly := False;
      acRevert.Enabled := True;
    end
    else
    begin
      // Clear XML and disable updates
      FXMLEditor.Lines.Clear;
      FXMLEditor.ReadOnly := True;
      acApply.Enabled := False;
      acRevert.Enabled := False;
    end;
  finally
    FXMLEditor.Lines.EndUpdate;
    FModifyingXML := False;
  end
end;

procedure TAHMDesignerForm.UpdateItemMenus(Item: TAHMUIObject);
var
  i, j, k, l: Integer;
  MenuItem: TMenuItem;
begin
  // Fix compiler warnings
  j := 0; k := 0; l := 0;

  // Remove any existing custom menu items
  for i := Pred(miEdit.Count) downto 0 do
    if miEdit.Items[i].Action = acEdit then j := i + 1
    else if miEdit.Items[i].Tag > 0 then miEdit.Delete(i);

  for i := Pred(popTreeObjects.Items.Count) downto 0 do
    if popTreeObjects.Items[i].Action = acEdit then k := i + 1
    else if popTreeObjects.Items[i].Tag > 0 then popTreeObjects.Items.Delete(i);

  for i := Pred(popPreview.Items.Count) downto 0 do
    if popPreview.Items[i].Action = acEdit then l := i + 1
    else if popPreview.Items[i].Tag > 0 then popPreview.Items.Delete(i);

  // Now insert any new custom menu items
  if Assigned(Item) then
    for i := 1 to Pred(Item.ModifyActions) do
    begin
      MenuItem := TMenuItem.Create(mmMainMenu);
      MenuItem.Caption := Item.ModifyCaption(i);
      MenuItem.OnClick := miCustomActionClick;
      MenuItem.Tag := i;
      miEdit.Insert(j, MenuItem);
      Inc(j);

      MenuItem := TMenuItem.Create(popTreeObjects);
      MenuItem.Caption := Item.ModifyCaption(i);
      MenuItem.OnClick := miCustomActionClick;
      MenuItem.Tag := i;
      popTreeObjects.Items.Insert(k, MenuItem);
      Inc(k);

      MenuItem := TMenuItem.Create(popPreview);
      MenuItem.Caption := Item.ModifyCaption(i);
      MenuItem.OnClick := miCustomActionClick;
      MenuItem.Tag := i;
      popPreview.Items.Insert(l, MenuItem);
      Inc(l);
    end;
end;

procedure TAHMDesignerForm.UpdateHistoryMenu;
var
  i, j: Integer;
  Item: TAHMUIObject;
  MenuItem: TMenuItem;
begin
  // Fix compiler warnings
  j := 0;

  // Remove any existing custom menu items
  for i := Pred(miHistory.Count) downto 0 do
    if miHistory.Items[i] = miHistoryRoot then j := i + 1
    else if miHistory.Items[i].Tag > 0 then miHistory.Delete(i);

  // Now insert any new custom menu items
  for i := 0 to Pred(FManager.Navigation.Count) do
  begin
    Item := FManager.Navigation[i];
    MenuItem := TMenuItem.Create(mmMainMenu);
    MenuItem.Caption := Item.ClassName + ' ' + Item.Name;
    MenuItem.OnClick := miHistoryItemClick;
    MenuItem.Tag := i + 1;
    MenuItem.RadioItem := True;
    MenuItem.Checked := FManager.Navigation.Position = i;
    miHistory.Insert(j, MenuItem);
    Inc(j);
  end;
end;

procedure TAHMDesignerForm.RefreshDescriptor(ClearDisplay: Boolean = false);
var
  Node: PVirtualNode;
begin
  // Clear object inspector and XML view if required
  if ClearDisplay then
  begin
    FInspector.Clear;
    UpdateXML(nil);
  end;

  // We need to refresh all of the top level nodes
  FUpdatingTree := True;
  FThemeTree.BeginUpdate;
  try
    Node := FThemeTree.GetFirst;
    FThemeTree.ReInitNode(Node, True); // Descriptors
    Node := Node.NextSibling;
    FThemeTree.ReInitNode(Node, True); // Visual
    Node := Node.NextSibling;
    FThemeTree.ReInitNode(Node, True); // Interface
    Node := Node.NextSibling;
    FThemeTree.ReInitNode(Node, True); // Audio
    Node := Node.NextSibling;
    FThemeTree.ReInitNode(Node, True); // Language
  finally
    FThemeTree.EndUpdate;
    FThemeTree.Invalidate;
    FUpdatingTree := False;
  end;

  // Update form caption to reflect descriptor name
  UpdateFormCaption;
end;

procedure TAHMDesignerForm.FindSelection(SearchText: String);

  function SearchChildren(Root: PVirtualNode; const MatchText: String; var FirstMatch, Ignore: PVirtualNode): PVirtualNode;
  var
    Child: PVirtualNode;
    FoundText: String;
  begin
    // Set default result - not found
    Result := nil;

    while Root <> nil do
    begin
      // If we haven't found first match, or we already found our ignore
      // node then we need to compare node caption and description
      if (FirstMatch = nil) or (Ignore = nil) then
      begin
        FoundText := FThemeTree.Text[Root, 0] + FThemeTree.Text[Root, 1];
        if Pos(MatchText, UpperCase(FoundText)) > 0 then
        begin
          // If we have already found ignore node, then we can quit looking
          if Ignore = nil then
          begin
            Result := Root;
            Exit;
          end;

          // If this is the first match then record it in case we loop
          if FirstMatch = nil then FirstMatch := Root;
        end;
      end;

      // If this is the node we found last time then we can start looking
      if Ignore = Root then Ignore := nil;

      // No match yet, so look in child nodes
      Child := FThemeTree.GetFirstChild(Root);
      while Child <> nil do
      begin
        Result := SearchChildren(Child, MatchText, FirstMatch, Ignore);
        if Result = nil then Child := Child.NextSibling
                        else Exit;
      end;

      // No match yet, try sibling nodes
      Root := Root.NextSibling;
    end;
  end;
  
var
  Node, Ignore, FirstMatch: PVirtualNode;
begin
  try
    // Start searching from current selection if any
    Ignore := FThemeTree.GetFirstSelected;
    FirstMatch := nil;

    // Walk the tree from visuals down looking for a match
    Node := FThemeTree.GetFirst.NextSibling;
    Node := SearchChildren(Node, UpperCase(SearchText), FirstMatch, Ignore);

    // If search looped past end then return the first match
    if Node = nil then Node := FirstMatch;

    if Node <> nil then
    begin
      // Found a match, so update selection
      FThemeTree.ClearSelection;
      FThemeTree.Selected[Node] := True;
      FThemeTree.FocusedNode := Node;
      FThemeTree.ScrollIntoView(Node, False);
    end;
  finally
    // Force a repaint of the treeview
    FThemeTree.Invalidate;
  end;
end;

procedure TAHMDesignerForm.UpdateSelection(Selection: TAHMUIObject; Rebuild: Boolean = False);
var
  Desc, Node, Child: PVirtualNode;
  Data: PElementData;
  List: TAHMUIObjectList;
begin
  // Must have an assigned UIObject
  if not Assigned(Selection) then Exit;

  try
    // Iterate through each descriptor - start with visuals
    Desc := FThemeTree.GetFirst;
    Desc := Desc.NextSibling;

    while Desc <> nil do
    begin
      // Iterate through nodes looking for appropriate parent
      Node := FThemeTree.GetFirstChild(Desc);
      Data := FThemeTree.GetNodeData(Desc);

      // Separate check for plugins - these are only one level deep in the tree
      if (Data.Category = etPlugin) and (Selection.ObjectType = otPlugins) then
      begin
        while Node <> nil do
          if FManager.Plugins[Node.Index].Name = Selection.Name then
          begin
            // Found it, so update selection
            FThemeTree.ClearSelection;
            FThemeTree.Selected[Node] := True;
            FThemeTree.FocusedNode := Node;
            FThemeTree.ScrollIntoView(Node, False);
            Exit;
          end
          else
            Node := FThemeTree.GetNextSibling(Node);

        // Not found in our list of plugins
        Exit;
      end;

      // Not a plugin, so check each of the theme object lists
      while Node <> nil do
      begin
        Data := FThemeTree.GetNodeData(Node);

        // Check for parent node of matching object type
        if (Data.Action <> nil) and
           (TAHMUIObjectType(Data.Action.Tag) = Selection.ObjectType) then
        begin
          // Found right parent, refresh if necessary
          if Rebuild then FThemeTree.ReinitNode(Node, False);

          FThemeTree.Expanded[Node] := True;
          Child := Node.FirstChild;

          // Look for matching object name in list
          List := FManager.ObjectList[Selection.ObjectType];
          while Child <> nil do
            if List[Child.Index].Name = Selection.Name then
            begin
              // Found it, so update selection
              FThemeTree.ClearSelection;
              FThemeTree.Selected[Child] := True;
              FThemeTree.FocusedNode := Child;
              FThemeTree.ScrollIntoView(Child, False);
              Exit;
            end
            else
              Child := Child.NextSibling;

          // Not found in our list of items
          Exit;
        end;

        // Check other parents
        Node := FThemeTree.GetNextSibling(Node);
      end;

      // Check other descriptors
      Desc := Desc.NextSibling;
    end;
  finally
    // Force a repaint of the treeview
    FThemeTree.Invalidate;
  end;
end;

procedure TAHMDesignerForm.UpdateSelection(Descriptor: String);
var
  Node: PVirtualNode;
begin
  try
    // Iterate through descriptor nodes looking for matching name
    Node := FThemeTree.RootNode;
    FThemeTree.Expanded[Node] := True;
    Node := Node.FirstChild;
    FThemeTree.Expanded[Node] := True;
    Node := Node.FirstChild;

    while Node <> nil do
      if FManager.ThemeModules[Node.Index] = Descriptor then
      begin
        FThemeTree.ClearSelection;
        FThemeTree.Selected[Node] := True;
        FThemeTree.FocusedNode := Node;
        FThemeTree.ScrollIntoView(Node, False);
        Exit;
      end
      else
        Node := Node.NextSibling;
  finally
    FThemeTree.Invalidate;
  end;
end;

procedure TAHMDesignerForm.UpdatePreviewTabs;
var
  i: Integer;
begin
  // Refresh renderer tab history
  with tcRenderer do
  begin
    for i := Pred(Tabs.Count) downto 1 do Tabs.Delete(i);
    for i := 0 to Pred(FManager.Previews.Count) do
      Tabs.AddObject(FManager.Previews[i].Name, FManager.Previews[i]);
    if TabIndex < 0 then TabIndex := 0;
  end;
end;

//-----------------------------------------------------------------------------
//----------------------- Custom Controls Initialisation ----------------------
//-----------------------------------------------------------------------------

procedure TAHMDesignerForm.BuildCustomControls;
begin
  // Create and populate PNG image lists
  FLargeToolbarImages := TPngImageList.Create(Self);
  FSmallToolbarImages := TPngImageList.Create(Self);
  FLargeTreeImages := TPngImageList.Create(Self);
  FSmallTreeImages := TPngImageList.Create(Self);

  // Create and configure virtual tree
  FThemeTree := TVirtualStringTree.Create(Self);
  with FThemeTree do
  begin
    Parent := pnDescriptorList;
    Align := alClient;
    DragType := dtVCL;
    Images := FLargeTreeImages;
    StateImages := FSmallTreeImages;
    Header.Options := [hoColumnResize, hoDrag, hoShowHint, hoVisible];
    TreeOptions.AutoOptions := [toAutoDropExpand, toAutoExpand,
                                toAutoScrollOnExpand, toAutoSpanColumns,
                                toAutoTristateTracking, toAutoDeleteMovedNodes];
    TreeOptions.MiscOptions := [toAcceptOLEDrop, toCheckSupport,
                                toFullRepaintOnResize, toGridExtensions,
                                toInitOnSave, toReportMode, toToggleOnDblClick,
                                toWheelPanning, toVariableNodeHeight];
    TreeOptions.SelectionOptions := [toFullRowSelect, toLevelSelectConstraint];
    OnChange := vstThemeChange;
    OnChecked := vstThemeChecked;
    OnContextPopup := vstThemeContextPopup;
    OnDblClick := vstThemeDblClick;
    OnDragAllowed := vstThemeDragAllowed;
    OnEndDrag := vstThemeEndDrag;
    OnGetText := vstThemeGetText;
    OnPaintText := vstThemePaintText;
    OnGetImageIndex := vstThemeGetImageIndex;
    OnGetNodeDataSize := vstThemeGetNodeDataSize;
    OnInitChildren := vstThemeInitChildren;
    OnInitNode := vstThemeInitNode;
    OnMeasureItem := vstThemeMeasureItem;
    OnStartDrag := vstThemeStartDrag;
    with Header.Columns.Add do Width := 150; Header.Columns[0].Text := 'Name';
    with Header.Columns.Add do Width := 220; Header.Columns[1].Text := 'Description';
  end;

  // Create and configure object inspector
  FInspector := TJvInspector.Create(Self);
  with FInspector do
  begin
    Align := alClient;
    Parent := pnProperties;
    Style := isDotNet;
    Divider := 118;
    OnItemSelected := InspectorItemSelected;
    OnItemValueChanged := InspectorItemValueChanged;
    OnItemValueChanging := InspectorItemValueChanging;
    OnItemDoubleClicked := InspectorItemDoubleClicked;
  end;

  // Create and configure XML editor
  FXMLEditor := TJvHLEditor.Create(Self);
  with FXMLEditor do
  begin
    Align := alClient;
    Parent := tsXML;
    Highlighter := hlHtml;
    GutterWidth := 30;
    Font.Color := clActiveBorder;
    Font.Height := -11;
    Font.Style := [fsItalic];
    TabStops := '3 7';
    RightMarginVisible := False;
    ReadOnly := True;
    Colors.Comment.ForeColor := clSkyBlue;
    Colors.Number.ForeColor := clNavy;
    Colors.Strings.ForeColor := clNavy;
    Colors.Statement.ForeColor := clGray;
    Colors.Symbol.ForeColor := clBlue;
    Colors.Reserved.ForeColor := clGray;
    Colors.Identifier.ForeColor := clBlue;
    OnChange := mmXMLChange;
    OnPaintGutter := mmXMLPaintGutter;
  end;
end;

procedure TAHMDesignerForm.BuildToolbars;
  function AddSeparator(Toolbar: TToolbar): TToolButton;
  begin
    Result := TToolButton.Create(Toolbar);
    Result.Parent := Toolbar;
    Result.Style := tbsSeparator;
    Result.Width := 8;
    Result.Left := Toolbar.Buttons[Toolbar.ButtonCount - 1].Left +
                   Toolbar.ButtonWidth;
  end;

  function AddButton(Toolbar: TToolbar; ButtonAction: TAction): TToolButton;
  begin
    Result := TToolButton.Create(Toolbar);
    Result.Parent := Toolbar;
    Result.Action := ButtonAction;
    Result.Left := Toolbar.Buttons[Toolbar.ButtonCount - 1].Left +
                   Toolbar.ButtonWidth;
  end;

begin
  // Build top toolbar buttons
  AddButton(tbTopToolbar, acNew);
  AddButton(tbTopToolbar, acOpen);
  AddButton(tbTopToolbar, acSave);
  AddButton(tbTopToolbar, acSaveAs);
  AddButton(tbTopToolbar, acThemeProperties);
  AddSeparator(tbTopToolbar);
  AddButton(tbTopToolbar, acUndo);
  AddButton(tbTopToolbar, acRedo);
  AddSeparator(tbTopToolbar);
  AddButton(tbTopToolbar, acCut);
  AddButton(tbTopToolbar, acCopy);
  AddButton(tbTopToolbar, acPaste);
  AddSeparator(tbTopToolbar);
  AddButton(tbTopToolbar, acNewObject);
  AddButton(tbTopToolbar, acEdit);
  AddButton(tbTopToolbar, acRenameObject);
  AddButton(tbTopToolbar, acDelete);
  AddButton(tbTopToolbar, acMoveObject);
  AddSeparator(tbTopToolbar);
  AddButton(tbTopToolbar, acFindObject);
  AddButton(tbTopToolbar, acFindAgain);
  AddSeparator(tbTopToolbar);
  AddButton(tbTopToolbar, acBaseDescriptor);
  AddSeparator(tbTopToolbar);
  AddButton(tbTopToolbar, acBack);
  AddButton(tbTopToolbar, acForward);
  AddSeparator(tbTopToolbar);
  AddButton(tbTopToolbar, acRestart);
  AddSeparator(tbTopToolbar);
  AddButton(tbTopToolbar, acExit);

  // Build preview toolbar buttons
  AddButton(tbMediateToolbar, acMouseTool);
  AddSeparator(tbMediateToolbar);
  AddButton(tbMediateToolbar, acEditTool);
  AddButton(tbMediateToolbar, acRotateTool);
  AddSeparator(tbMediateToolbar);
  AddButton(tbMediateToolbar, acNewControl);
  AddButton(tbMediateToolbar, acNewContainer);
  AddButton(tbMediateToolbar, acNewScreen);
  AddButton(tbMediateToolbar, acRemoveSelection);
  AddSeparator(tbMediateToolbar);
  AddButton(tbMediateToolbar, acPanTool);
  AddButton(tbMediateToolbar, acZoomFull);
  AddButton(tbMediateToolbar, acZoomIn);
  AddButton(tbMediateToolbar, acZoomOut);
  AddSeparator(tbMediateToolbar);
  AddButton(tbMediateToolbar, acBorders);
end;

procedure TAHMDesignerForm.FixControlStyles(Parent: TControl);
var
  i: Integer;
begin
  // Tweak control styles recursively to display drag images
  Parent.ControlStyle := Parent.ControlStyle + [csDisplayDragImage];
  if Parent is TWinControl then
    with TWinControl(Parent) do
      for i := 0 to Pred(ControlCount) do
        FixControlStyles(Controls[i]);
end;

procedure TAHMDesignerForm.LoadImages;
begin
  with FLargeToolbarImages do
  begin
    Clear;
    Width := 32; Height := 32;

    BeginUpdate;
    AddResourcePng('LG_IMG_EXIT');         //0
    AddResourcePng('LG_IMG_ABOUT');        //1
    AddResourcePng('LG_IMG_INTERNET');     //2
    AddResourcePng('LG_IMG_COPY');         //3
    AddResourcePng('LG_IMG_CUT');          //4
    AddResourcePng('LG_IMG_DELETE');       //5
    AddResourcePng('LG_IMG_EDIT');         //6
    AddResourcePng('LG_IMG_NEW_THEME');    //7
    AddResourcePng('LG_IMG_OPEN_THEME');   //8
    AddResourcePng('LG_IMG_PASTE');        //9
    AddResourcePng('LG_IMG_SAVE');         //10
    AddResourcePng('LG_IMG_SAVEAS');       //11
    AddResourcePng('LG_IMG_CONFIGURE');    //12
    AddResourcePng('LG_IMG_RESTART');      //13
    AddResourcePng('LG_IMG_SHUTDOWN');     //14
    AddResourcePng('LG_IMG_LAUNCH');       //15
    AddResourcePng('LG_IMG_TOOL_MOUSE');   //16
    AddResourcePng('LG_IMG_TOOL_CNTNRS');  //17
    AddResourcePng('LG_IMG_TOOL_CNTRLS');  //18
    AddResourcePng('LG_IMG_TOOL_SCREENS'); //19
    AddResourcePng('LG_IMG_TOOL_PLACES');  //20
    AddResourcePng('LG_IMG_TOOL_ROTATE');  //21
    AddResourcePng('LG_IMG_NEW_OBJECT');   //22
    AddResourcePng('LG_IMG_BASE_DESC');    //23
    AddResourcePng('LG_IMG_REDO');         //24
    AddResourcePng('LG_IMG_UNDO');         //25
    AddResourcePng('LG_IMG_SHOW_GRID');    //26 - not currently used (for grid display later)
    AddResourcePng('LG_IMG_PREVIOUS');     //27
    AddResourcePng('LG_IMG_NEXT');         //28
    AddResourcePng('LG_IMG_PAUSE');        //29
    AddResourcePng('LG_IMG_SEARCH');       //30
    AddResourcePng('LG_IMG_TOOL_DESIGN');  //31
    AddResourcePng('LG_IMG_LOCK_VIEW');    //32 - not currently used
    AddResourcePng('LG_IMG_VIEW_ERRORS');  //33
    AddResourcePng('LG_IMG_TOOL_OUTLINE'); //34
    AddResourcePng('LG_IMG_EDIT_THEME');   //35
    AddResourcePng('LG_IMG_RENAME');       //36
    AddResourcePng('LG_IMG_MOVE');         //37
    AddResourcePng('LG_IMG_FIND_AGAIN');   //38
    AddResourcePng('LG_IMG_TOOL_REMOVE');  //39
    AddResourcePng('LG_IMG_TOOL_ZOOMIN');  //40
    AddResourcePng('LG_IMG_TOOL_ZOOMOUT'); //41
    AddResourcePng('LG_IMG_TOOL_NOZOOM');  //42
    AddResourcePng('LG_IMG_TOOL_PAN');     //43
    EndUpdate;
  end;

  with FSmallToolbarImages do
  begin
    Clear;
    Width := 16; Height := 16;

    BeginUpdate;
    AddResourcePng('SM_IMG_EXIT');         //0
    AddResourcePng('SM_IMG_ABOUT');        //1
    AddResourcePng('SM_IMG_INTERNET');     //2
    AddResourcePng('SM_IMG_COPY');         //3
    AddResourcePng('SM_IMG_CUT');          //4
    AddResourcePng('SM_IMG_DELETE');       //5
    AddResourcePng('SM_IMG_EDIT');         //6
    AddResourcePng('SM_IMG_NEW_THEME');    //7
    AddResourcePng('SM_IMG_OPEN_THEME');   //8
    AddResourcePng('SM_IMG_PASTE');        //9
    AddResourcePng('SM_IMG_SAVE');         //10
    AddResourcePng('SM_IMG_SAVEAS');       //11
    AddResourcePng('SM_IMG_CONFIGURE');    //12
    AddResourcePng('SM_IMG_RESTART');      //13
    AddResourcePng('SM_IMG_SHUTDOWN');     //14
    AddResourcePng('SM_IMG_LAUNCH');       //15
    AddResourcePng('SM_IMG_TOOL_MOUSE');   //16
    AddResourcePng('SM_IMG_TOOL_CNTNRS');  //17
    AddResourcePng('SM_IMG_TOOL_CNTRLS');  //18
    AddResourcePng('SM_IMG_TOOL_SCREENS'); //19
    AddResourcePng('SM_IMG_TOOL_PLACES');  //20
    AddResourcePng('SM_IMG_TOOL_ROTATE');  //21
    AddResourcePng('SM_IMG_NEW_OBJECT');   //22
    AddResourcePng('SM_IMG_BASE_DESC');    //23
    AddResourcePng('SM_IMG_REDO');         //24
    AddResourcePng('SM_IMG_UNDO');         //25
    AddResourcePng('SM_IMG_SHOW_GRID');    //26 - not currently used (for grid display later)
    AddResourcePng('SM_IMG_PREVIOUS');     //27
    AddResourcePng('SM_IMG_NEXT');         //28
    AddResourcePng('SM_IMG_PAUSE');        //29
    AddResourcePng('SM_IMG_SEARCH');       //30
    AddResourcePng('SM_IMG_TOOL_DESIGN');  //31
    AddResourcePng('SM_IMG_LOCK_VIEW');    //32 - not currently used
    AddResourcePng('SM_IMG_VIEW_ERRORS');  //33
    AddResourcePng('SM_IMG_TOOL_OUTLINE'); //34
    AddResourcePng('SM_IMG_EDIT_THEME');   //35
    AddResourcePng('SM_IMG_RENAME');       //36
    AddResourcePng('SM_IMG_MOVE');         //37
    AddResourcePng('SM_IMG_FIND_AGAIN');   //38
    AddResourcePng('SM_IMG_TOOL_REMOVE');  //39
    AddResourcePng('SM_IMG_TOOL_ZOOMIN');  //40
    AddResourcePng('SM_IMG_TOOL_ZOOMOUT'); //41
    AddResourcePng('SM_IMG_TOOL_NOZOOM');  //42
    AddResourcePng('SM_IMG_TOOL_PAN');     //43
    EndUpdate;
  end;

  with FLargeTreeImages do
  begin
    Clear;
    Width := 32; Height := 32;

    // Descriptor icons
    BeginUpdate;
    AddResourcePng('LG_IMG_BASE_DESC');    //0
    AddResourcePng('LG_IMG_CUST_DESC');    //1
    AddResourcePng('LG_IMG_LIB_DESC');     //2
    AddResourcePng('LG_IMG_WARNING');      //3
    AddResourcePng('LG_IMG_ERROR');        //4

    // Control icons
    AddResourcePng('LG_IMG_BARS');         //0
    AddResourcePng('LG_IMG_BUTTONS');      //1
    AddResourcePng('LG_IMG_CAPTIONS');     //2
    AddResourcePng('LG_IMG_CHECKBUTTON');  //3
    AddResourcePng('LG_IMG_COMBOBUTTON');  //4
    AddResourcePng('LG_IMG_BITMAPS');      //5
    AddResourcePng('LG_IMG_GRIDS');        //6
    AddResourcePng('LG_IMG_MEDIAWINDOWS'); //7
    AddResourcePng('LG_IMG_PLUGIN');       //8
    AddResourcePng('LG_IMG_RADIOBUTTON');  //9
    AddResourcePng('LG_IMG_SHAPES');       //10
    AddResourcePng('LG_IMG_TEXTEDITS');    //11
    AddResourcePng('LG_IMG_CLONE');        //12
    AddResourcePng('LG_IMG_GAUGES');       //13
    AddResourcePng('LG_IMG_DIALS');        //14

    // Theme object icons
    AddResourcePng('LG_IMG_COLOURS');      //0
    AddResourcePng('LG_IMG_FILLS');        //1
    AddResourcePng('LG_IMG_PLASMA');       //2
    AddResourcePng('LG_IMG_FONTS');        //3
    AddResourcePng('LG_IMG_BITMAPS');      //4
    AddResourcePng('LG_IMG_BACKGROUNDS');  //5
    AddResourcePng('LG_IMG_FOREGROUNDS');  //6
    AddResourcePng('LG_IMG_SHAPES');       //7
    AddResourcePng('LG_IMG_CAPTIONS');     //8
    AddResourcePng('LG_IMG_MEDIAWINDOWS'); //9
    AddResourcePng('LG_IMG_BARS');         //10
    AddResourcePng('LG_IMG_BUTTONS');      //11
    AddResourcePng('LG_IMG_MEDIAGRIDS');   //12
    AddResourcePng('LG_IMG_FUNCTIONS');    //13
    AddResourcePng('LG_IMG_EXPRESSIONS');  //14
    AddResourcePng('LG_IMG_MACROS');       //15
    AddResourcePng('LG_IMG_ACTIONS');      //16
    AddResourcePng('LG_IMG_METAINFO');     //17
    AddResourcePng('LG_IMG_PLACES');       //18
    AddResourcePng('LG_IMG_ANIMATORS');    //19
    AddResourcePng('LG_IMG_LAYOUTS');      //20
    AddResourcePng('LG_IMG_CONTROLS');     //21
    AddResourcePng('LG_IMG_CONTAINERS');   //22
    AddResourcePng('LG_IMG_SCREENS');      //23
    AddResourcePng('LG_IMG_SOUNDS');       //24
    AddResourcePng('LG_IMG_LANGUAGES');    //25
    AddResourcePng('LG_IMG_GRIDS');        //26
    AddResourcePng('LG_IMG_TEXTEDITS');    //27
    AddResourcePng('LG_IMG_TIMERS');       //28
    AddResourcePng('LG_IMG_EFFECTS');      //29
    AddResourcePng('LG_IMG_PLUGIN');       //30
    AddResourcePng('LG_IMG_GAUGES');       //31
    AddResourcePng('LG_IMG_DIALS');        //32
    AddResourcePng('LG_IMG_GRIDITEMS');    //33
    EndUpdate;
  end;

  with FSmallTreeImages do
  begin
    Clear;
    Width := 16; Height := 16;

    // Descriptor icons
    BeginUpdate;
    AddResourcePng('SM_IMG_BASE_DESC');    //0
    AddResourcePng('SM_IMG_CUST_DESC');    //1
    AddResourcePng('SM_IMG_LIB_DESC');     //2
    AddResourcePng('SM_IMG_WARNING');      //3
    AddResourcePng('SM_IMG_ERROR');        //4

    // Control icons
    AddResourcePng('SM_IMG_BARS');         //0
    AddResourcePng('SM_IMG_BUTTONS');      //1
    AddResourcePng('SM_IMG_CAPTIONS');     //2
    AddResourcePng('SM_IMG_CHECKBUTTON');  //3
    AddResourcePng('SM_IMG_COMBOBUTTON');  //4
    AddResourcePng('SM_IMG_BITMAPS');      //5
    AddResourcePng('SM_IMG_GRIDS');        //6
    AddResourcePng('SM_IMG_MEDIAWINDOWS'); //7
    AddResourcePng('SM_IMG_PLUGIN');       //8
    AddResourcePng('SM_IMG_RADIOBUTTON');  //9
    AddResourcePng('SM_IMG_SHAPES');       //10
    AddResourcePng('SM_IMG_TEXTEDITS');    //11
    AddResourcePng('SM_IMG_CLONE');        //12
    AddResourcePng('SM_IMG_GAUGES');       //13
    AddResourcePng('SM_IMG_DIALS');        //14

    // Theme object icons
    AddResourcePng('SM_IMG_COLOURS');      //0
    AddResourcePng('SM_IMG_FILLS');        //1
    AddResourcePng('SM_IMG_PLASMA');       //2
    AddResourcePng('SM_IMG_FONTS');        //3
    AddResourcePng('SM_IMG_BITMAPS');      //4
    AddResourcePng('SM_IMG_BACKGROUNDS');  //5
    AddResourcePng('SM_IMG_FOREGROUNDS');  //6
    AddResourcePng('SM_IMG_SHAPES');       //7
    AddResourcePng('SM_IMG_CAPTIONS');     //8
    AddResourcePng('SM_IMG_MEDIAWINDOWS'); //9
    AddResourcePng('SM_IMG_BARS');         //10
    AddResourcePng('SM_IMG_BUTTONS');      //11
    AddResourcePng('SM_IMG_MEDIAGRIDS');   //12
    AddResourcePng('SM_IMG_FUNCTIONS');    //13
    AddResourcePng('SM_IMG_EXPRESSIONS');  //14
    AddResourcePng('SM_IMG_MACROS');       //15
    AddResourcePng('SM_IMG_ACTIONS');      //16
    AddResourcePng('SM_IMG_METAINFO');     //17
    AddResourcePng('SM_IMG_PLACES');       //18
    AddResourcePng('SM_IMG_ANIMATORS');    //19
    AddResourcePng('SM_IMG_LAYOUTS');      //20
    AddResourcePng('SM_IMG_CONTROLS');     //21
    AddResourcePng('SM_IMG_CONTAINERS');   //22
    AddResourcePng('SM_IMG_SCREENS');      //23
    AddResourcePng('SM_IMG_SOUNDS');       //24
    AddResourcePng('SM_IMG_LANGUAGES');    //25
    AddResourcePng('SM_IMG_GRIDS');        //26
    AddResourcePng('SM_IMG_TEXTEDITS');    //27
    AddResourcePng('SM_IMG_TIMERS');       //28
    AddResourcePng('SM_IMG_EFFECTS');      //29
    AddResourcePng('SM_IMG_PLUGIN');       //30
    AddResourcePng('SM_IMG_GAUGES');       //31
    AddResourcePng('SM_IMG_DIALS');        //32
    AddResourcePng('SM_IMG_GRIDITEMS');    //33
    EndUpdate;
  end;
end;

//-----------------------------------------------------------------------------
//--------------------------- Preference Handling -----------------------------
//-----------------------------------------------------------------------------

procedure TAHMDesignerForm.ApplyPreferences(Loading: Boolean);
  procedure ConfigureToolbar(Toolbar: TToolbar; Large: Boolean);
  begin
    if Large then
    begin
      Toolbar.Images := FLargeToolbarImages;
      Toolbar.Height := 39;
      Toolbar.ButtonWidth := 35;
      Toolbar.ButtonHeight := 34;
    end
    else
    begin
      Toolbar.Images := FSmallToolbarImages;
      Toolbar.Height := 23;
      Toolbar.ButtonWidth := 19;
      Toolbar.ButtonHeight := 18;
    end;
  end;
var
  LayoutChanged: Boolean;
begin
  with FManager.Preferences do
  begin
    // Configure toolbars
    ConfigureToolbar(tbTopToolbar, LargeMainToolbar);
    ConfigureToolbar(tbMediateToolbar, LargePreviewToolbar);

    // Configure arrangement of treeview and properties panel
    if HorizontalLayout then LayoutChanged := pnDescriptorList.Align = alTop
                        else LayoutChanged := pnDescriptorList.Align = alLeft;
    pnDescriptorList.Align := alNone;
    SplitterProperties.Align := alNone;
    SplitterProperties.Width := 3;
    SplitterProperties.Height := 3;
    if HorizontalLayout then pnDescriptorList.Align := alLeft
                        else pnDescriptorList.Align := alTop;
    if HorizontalLayout then SplitterProperties.Align := alLeft
                        else SplitterProperties.Align := alTop;
    if LayoutChanged then
      if HorizontalLayout then pnDescriptorList.Width := pnLeftPanel.Width div 2
                          else pnDescriptorList.Height := pnLeftPanel.Height div 2;

    // Perform additional preference setup when first loading designer
    if Loading then
    begin
      // Configure form position and splitter arrangement
      MainFormState.RestoreFormState(Self);

      // Auto load last theme name if set in preferences
      if AutoOpenThemeName <> '' then
        if FManager.OpenTheme(AutoOpenThemeName) then
          InitialiseTheme;
    end;

    // Force refresh of mediate panel size calculation
    pnMediateFrameResize(pnMediateFrame);
  end;
end;

//-----------------------------------------------------------------------------
//------------------------------ Uncategorised --------------------------------
//-----------------------------------------------------------------------------

end.
